This code is running using R notebook in RStudio

Goals:

We are collecting a dataset on water quality to train a machine learning model for binary classification: determining whether water is safe for consumption (1) or not (0). This model will help with water treatment decisions and ensure compliance with quality standards. We applied different Summarzition and plotting methods to help us to understand our dataset, such as scatter, histogram and bar plot. Then, we applyed preprocess in our data using data cleaning, data transformation and feature selection.

Classification and Clustering Goal:

1-classification in this dataset is to build a predictive model that can classify water samples into two categories: potable (suitable for consumption) or non-potable (not suitable for consumption).

2-clustering in this dataset is to identify natural groupings or clusters within the water samples based on their quality parameters.

Source of the dataset:

Kaggle

library:

#install.packages("caret")
#install.packages("glmnet")
#install.packages("Boruta")
#install.packages("mlbench")
#install.packages("randomForest")
library(cluster)
library(factoextra) 
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(outliers)
library(dplyr)
library(mlbench)
library(caret)
library(glmnet)
library(Boruta)
library(ggplot2)
library(randomForest)
library(pROC)
library(e1071)
library(caret)
library(party)
library(partykit)
library(RWeka)
library(C50)
library(printr)
library(rpart)
library(rpart.plot)

getwd()
[1] "/Users/mahayie/Documents/GitHub/DM1Project"
#setwd("/Users/mahayie/Desktop/326p")
#getwd()

water_potability = read.csv('Dataset/water_potability.csv')

str(water_potability)
'data.frame':   3276 obs. of  10 variables:
 $ ph             : num  NA 3.72 8.1 8.32 9.09 ...
 $ Hardness       : num  205 129 224 214 181 ...
 $ Solids         : num  20791 18630 19910 22018 17979 ...
 $ Chloramines    : num  7.3 6.64 9.28 8.06 6.55 ...
 $ Sulfate        : num  369 NA NA 357 310 ...
 $ Conductivity   : num  564 593 419 363 398 ...
 $ Organic_carbon : num  10.4 15.2 16.9 18.4 11.6 ...
 $ Trihalomethanes: num  87 56.3 66.4 100.3 32 ...
 $ Turbidity      : num  2.96 4.5 3.06 4.63 4.08 ...
 $ Potability     : int  0 0 0 0 0 0 0 0 0 0 ...

sample of data

sample of raw dataset(first 10 rows):


head(water_potability,10)

sample of raw dataset(last 10 rows):

tail(water_potability, 10)

Five number summary of each attribute in our dataset:

summary(water_potability)
       ph         Hardness       Solids       Chloramines       Sulfate     Conductivity Organic_carbon
 Min.   : 0    Min.   : 47   Min.   :  321   Min.   : 0.35   Min.   :129   Min.   :181   Min.   : 2.2  
 1st Qu.: 6    1st Qu.:177   1st Qu.:15667   1st Qu.: 6.13   1st Qu.:308   1st Qu.:366   1st Qu.:12.1  
 Median : 7    Median :197   Median :20928   Median : 7.13   Median :333   Median :422   Median :14.2  
 Mean   : 7    Mean   :196   Mean   :22014   Mean   : 7.12   Mean   :334   Mean   :426   Mean   :14.3  
 3rd Qu.: 8    3rd Qu.:217   3rd Qu.:27333   3rd Qu.: 8.11   3rd Qu.:360   3rd Qu.:482   3rd Qu.:16.6  
 Max.   :14    Max.   :323   Max.   :61227   Max.   :13.13   Max.   :481   Max.   :753   Max.   :28.3  
 NA's   :491                                                 NA's   :781                               
 Trihalomethanes   Turbidity      Potability  
 Min.   :  0.7   Min.   :1.45   Min.   :0.00  
 1st Qu.: 55.8   1st Qu.:3.44   1st Qu.:0.00  
 Median : 66.6   Median :3.96   Median :0.00  
 Mean   : 66.4   Mean   :3.97   Mean   :0.39  
 3rd Qu.: 77.3   3rd Qu.:4.50   3rd Qu.:1.00  
 Max.   :124.0   Max.   :6.74   Max.   :1.00  
 NA's   :162                                  

Number of column and rows

dim(water_potability)
[1] 3276   10

Sample of Water_potability dataset

This is a sample of the dataset to help to understand how it is structured and organized

View(water_potability)
sample(water_potability)

Checking for missing values:

The absence of data in certain variables or columns in a dataset is referred to as missing or null values due to various reasons. It can have a negative impact on the dataset’s efficiency and the information that can be taken from it later, so we checked to see whether our data had missing or null values and eliminated these rows to produce a more efficient dataset.

first we checked for missing value to ensures accurate statistics, reliable visualizations, and guides decisions on imputation or removal of missing data.

dim(water_potability)
[1] 3276   10
sum(is.na(water_potability))
[1] 1434

Remove rows with missing values

colSums(is.na(water_potability))
             ph        Hardness          Solids     Chloramines         Sulfate    Conductivity  Organic_carbon 
            491               0               0               0             781               0               0 
Trihalomethanes       Turbidity      Potability 
            162               0               0 
water_potability = na.omit(water_potability)
colSums(is.na(water_potability))
             ph        Hardness          Solids     Chloramines         Sulfate    Conductivity  Organic_carbon 
              0               0               0               0               0               0               0 
Trihalomethanes       Turbidity      Potability 
              0               0               0 
View(water_potability)

second remove rows

Standard deviation:

The standard deviation in statistics is a measure used to assess the spread of data around the mean. It gives us an idea of how much the data points deviate from the average.

sd(water_potability$Turbidity)
[1] 0.78
sd(water_potability$Solids)
[1] 8642
sd(water_potability$Conductivity)
[1] 80.7
sd(water_potability$Organic_carbon)
[1] 3.32
sd(water_potability$ph)
[1] 1.57

we ues it for five coulme Turbidity,Solids,Conductivity,Organic_carbon,ph.

Mean:

the average, is a measure of central tendency in statistics. It is calculated by summing up all the values in a dataset and dividing by the number of values. The mean gives us a representative value that is typically used to describe the “typical” value in a set of data.

mean(water_potability$Turbidity)
[1] 3.97
mean(water_potability$Solids) 
[1] 21917
mean(water_potability$Conductivity) 
[1] 427
mean(water_potability$Organic_carbon) 
[1] 14.4
mean(water_potability$ph) 
[1] 7.09

we ues it for five coulme Turbidity,Solids,Conductivity,Organic_carbon,ph.

Median:

It represents the middle value in a dataset when the values are arranged in ascending or descending order.

median(water_potability$Turbidity)
[1] 3.97
median(water_potability$Solids)
[1] 20934
median(water_potability$Conductivity)
[1] 423
median(water_potability$Organic_carbon)
[1] 14.3
median(water_potability$ph)
[1] 7.03

we ues it for five coulme Turbidity,Solids,Conductivity,Organic_carbon,ph.

Variance:

It provides information about how far each value in the dataset is from the mean. A higher variance indicates a greater spread of data, while a lower variance suggests that the data points are closer to the mean.

var(water_potability$Turbidity)
[1] 0.609
var(water_potability$Solids)
[1] 74688309
var(water_potability$Conductivity)
[1] 6515
var(water_potability$Organic_carbon)
[1] 11.1
var(water_potability$ph)
[1] 2.48

As you can see the highest Variance Solids, and the lowest Turbidity.

Statistical Measures:

In the given code, you’ve used the summary function to obtain key statistical measures for variables such as “Conductivity,” “Organic_carbon,” “Hardness,” etc., in the water_potability dataset. These measures include minimum, 1st quartile, median, mean, 3rd quartile, and maximum values, offering a succinct overview of each variable’s distribution and characteristics. This provides essential insights for the initial exploration and understanding of the dataset.

With using minimum, maximum, mean, median laws it helps to provide an overview of the data’s key characteristics

summary(water_potability$Conductivity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    202     367     423     427     482     753 
summary(water_potability$Organic_carbon)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    2.2    12.1    14.3    14.4    16.7    27.0 
summary(water_potability$Hardness)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     73     177     197     196     216     317 
summary(water_potability$Solids)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    321   15616   20934   21917   27183   56489 
summary(water_potability$Chloramines)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.39    6.14    7.14    7.13    8.11   13.13 
summary(water_potability$Potability)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   0.000   0.000   0.403   1.000   1.000 
summary(water_potability$Sulfate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    129     308     332     333     359     481 
summary(water_potability$Trihalomethanes)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    8.6    56.0    66.5    66.4    77.3   124.0 
summary(water_potability$Turbidity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.45    3.44    3.97    3.97    4.51    6.49 
summary(water_potability$ph)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.23    6.09    7.03    7.09    8.05   14.00 

Data Transformation:

This step involved transforming the class label, Potability, into categorical data. We changed the numeric data to ‘Not Potable’ and ‘Potable’ to indicate whether the water is safe for human consumption, where 1 represents ‘Potable’, and 0 represents ’Not Potable.

water_potability$Potability[water_potability$Potability == '0'] <- 'Not Potable'
water_potability$Potability[water_potability$Potability == '1'] <- 'Potable'

water_potability$Potability <- as.factor(water_potability$Potability)
table(water_potability$Potability)

Not Potable     Potable 
       1200         811 
print(water_potability)

outliers:

They are observations that lie far away from the majority of the data. Outliers can occur due to various reasons such as measurement errors, experimental anomalies, or genuine extreme values.

##before removing outlier:

dim(water_potability)
[1] 2011   10
head(water_potability)

removing outliers:

Removing outliers from a dataset is critical for assuring the quality and reliability of statistical analysis and machine learning models. We found all outliers in the numerical attributes and subsequently eliminated the rows containing the outliers.

- ph

summary(water_potability$ph)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.23    6.09    7.03    7.09    8.05   14.00 
quartiles <- quantile(water_potability$ph, probs = c(.25, .75), na.rm = FALSE)
quartiles
 25%  75% 
6.09 8.05 
iqr <- IQR(water_potability$ph)
iqr
[1] 1.96
lower <- quartiles[1] - 1.5*iqr
lower
 25% 
3.14 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
 11 
boxplot(ph ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$ph, ylab = 'ph')$out
  out_val
  out_rows <- which(water_potability$ph %in% c(out_val))
  out_rows
  
  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$ph)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   3.23    6.11    7.03    7.09    8.03   10.91 
#-------------------------------------------

-Hardness

summary(water_potability$Hardness)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     73     177     197     196     216     317 
quartiles <- quantile(water_potability$Hardness, probs = c(.25, .75), na.rm = FALSE)
quartiles
25% 75% 
177 216 
iqr <- IQR(water_potability$Hardness)
iqr
[1] 39.5
lower <- quartiles[1] - 1.5*iqr
lower
25% 
118 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
276 
boxplot(Hardness ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Hardness, ylab = 'Hardness')$out
  out_val
  out_rows <- which(water_potability$Hardness %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Hardness)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    121     178     197     196     216     272 
#-------------------------------------------

-Solids

summary(water_potability$Solids)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    321   15704   20855   21840   27046   56489 
quartiles <- quantile(water_potability$Solids, probs = c(.25, .75), na.rm = FALSE)
quartiles
  25%   75% 
15704 27046 
iqr <- IQR(water_potability$Solids)
iqr
[1] 11341
lower <- quartiles[1] - 1.5*iqr
lower
  25% 
-1308 
upper <- quartiles[2] + 1.5*iqr
upper
  75% 
44058 
boxplot(Solids ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Solids, ylab = 'Solids')$out
  out_val
  out_rows <- which(water_potability$Solids %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Solids)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    321   15547   20519   21420   26735   43195 
#-------------------------------------------

-Chloramines

summary(water_potability$Chloramines)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.39    6.14    7.14    7.13    8.09   13.13 
quartiles <- quantile(water_potability$Chloramines, probs = c(.25, .75), na.rm = FALSE)
quartiles
 25%  75% 
6.14 8.09 
iqr <- IQR(water_potability$Chloramines)
iqr
[1] 1.95
lower <- quartiles[1] - 1.5*iqr
lower
 25% 
3.21 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
 11 
boxplot(Chloramines ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Chloramines, ylab = 'Chloramines')$out
  out_val
  out_rows <- which(water_potability$Chloramines %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Chloramines)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   3.35    6.18    7.14    7.14    8.08   10.90 
#-------------------------------------------

-Sulfate

summary(water_potability$Sulfate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    187     308     333     333     358     481 
quartiles <- quantile(water_potability$Sulfate, probs = c(.25, .75), na.rm = FALSE)
quartiles
25% 75% 
308 358 
iqr <- IQR(water_potability$Sulfate)
iqr
[1] 50.1
lower <- quartiles[1] - 1.5*iqr
lower
25% 
233 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
433 
boxplot(Sulfate ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Sulfate, ylab = 'Sulfate')$out
  out_val
  out_rows <- which(water_potability$Sulfate %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Sulfate)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    238     309     333     334     358     430 
#-------------------------------------------

-Conductivity

summary(water_potability$Conductivity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    202     367     424     427     483     753 
quartiles <- quantile(water_potability$Conductivity, probs = c(.25, .75), na.rm = FALSE)
quartiles
25% 75% 
367 483 
iqr <- IQR(water_potability$Conductivity)
iqr
[1] 116
lower <- quartiles[1] - 1.5*iqr
lower
25% 
192 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
657 
boxplot(Conductivity ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Conductivity, ylab = 'Conductivity')$out
  out_val
  out_rows <- which(water_potability$Conductivity %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Conductivity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    202     366     423     426     482     653 
#-------------------------------------------

-Organic_carbon

summary(water_potability$Organic_carbon)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   4.37   12.18   14.35   14.42   16.79   27.01 
quartiles <- quantile(water_potability$Organic_carbon, probs = c(.25, .75), na.rm = FALSE)
quartiles
 25%  75% 
12.2 16.8 
iqr <- IQR(water_potability$Organic_carbon)
iqr
[1] 4.6
lower <- quartiles[1] - 1.5*iqr
lower
 25% 
5.28 
upper <- quartiles[2] + 1.5*iqr
upper
 75% 
23.7 
boxplot(Organic_carbon ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Organic_carbon, ylab = 'Organic_carbon')$out
  out_val
  out_rows <- which(water_potability$Organic_carbon %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Organic_carbon)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   5.51   12.22   14.35   14.43   16.79   23.60 
#-------------------------------------------

-Trihalomethanes

summary(water_potability$Trihalomethanes)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    8.6    55.9    66.2    66.4    77.4   124.0 
quartiles <- quantile(water_potability$Trihalomethanes, probs = c(.25, .75), na.rm = FALSE)
quartiles
 25%  75% 
55.9 77.4 
iqr <- IQR(water_potability$Trihalomethanes)
iqr
[1] 21.6
lower <- quartiles[1] - 1.5*iqr
lower
 25% 
23.5 
upper <- quartiles[2] + 1.5*iqr
upper
75% 
110 
boxplot(Trihalomethanes ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Trihalomethanes, ylab = 'Trihalomethanes')$out
  out_val
  out_rows <- which(water_potability$Trihalomethanes %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Trihalomethanes)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   24.5    56.0    66.3    66.4    77.3   108.8 
#-------------------------------------------

-Turbidity

summary(water_potability$Turbidity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.45    3.44    3.97    3.97    4.52    6.49 
quartiles <- quantile(water_potability$Turbidity, probs = c(.25, .75), na.rm = FALSE)
quartiles
 25%  75% 
3.44 4.52 
iqr <- IQR(water_potability$Turbidity)
iqr
[1] 1.08
lower <- quartiles[1] - 1.5*iqr
lower
 25% 
1.82 
upper <- quartiles[2] + 1.5*iqr
upper
 75% 
6.14 
boxplot(Turbidity ~ Potability, data = water_potability)


repeat {
  out_val <- boxplot(water_potability$Turbidity, ylab = 'Turbidity')$out
  out_val
  out_rows <- which(water_potability$Turbidity %in% c(out_val))
  out_rows

  if(sum(out_rows) > 0) water_potability <- water_potability[-out_rows,]
  else {break}
}

summary(water_potability$Turbidity)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.87    3.44    3.97    3.97    4.51    6.08 

After removing outliers:

dim(water_potability)
[1] 1750   10
str(water_potability)
'data.frame':   1750 obs. of  10 variables:
 $ ph             : num  8.32 9.09 5.58 10.22 8.64 ...
 $ Hardness       : num  214 181 188 248 203 ...
 $ Solids         : num  22018 17979 28749 28750 13672 ...
 $ Chloramines    : num  8.06 6.55 7.54 7.51 4.56 ...
 $ Sulfate        : num  357 310 327 394 303 ...
 $ Conductivity   : num  363 398 280 284 475 ...
 $ Organic_carbon : num  18.4 11.6 8.4 13.8 12.4 ...
 $ Trihalomethanes: num  100.3 32 54.9 84.6 62.8 ...
 $ Turbidity      : num  4.63 4.08 2.56 2.67 4.4 ...
 $ Potability     : Factor w/ 2 levels "Not Potable",..: 1 1 1 1 1 1 1 1 1 1 ...
 - attr(*, "na.action")= 'omit' Named int [1:1265] 1 2 3 9 12 14 15 17 19 21 ...
  ..- attr(*, "names")= chr [1:1265] "1" "2" "3" "9" ...
head(water_potability)

Charts:

visual representation of data that help us understand and analyze information more easily. They can be used to display trends, comparisons, and relationships between different variables. There are various types of charts, such as

Histogram

The histogram shows the frequency of ph in the dataset; we noted that the majority of values fall within the usual range, which is about between 6 and 8, but it also shows several outliers.

hist(water_potability$ph)

hist(water_potability$Chloramines)

hist(water_potability$Hardness)

hist(water_potability$Solids)

hist(water_potability$Sulfate)

hist(water_potability$Conductivity)

hist(water_potability$Organic_carbon)

hist(water_potability$Trihalomethanes)

hist(water_potability$Turbidity)

Bar Plot

the bar plot represent how ph levels affect water portability in the dataset it indicates that ph level above 10 is not portibal and humans cant consume it

tab <- water_potability$Potability %>% table()
txt <- paste0(tab) 
bb <- water_potability$ph %>% table() %>% barplot( main='ph',col=c('pink'))

bb <- water_potability$Potability %>% table() %>% barplot( main='Potability',ylab='Frequency',col=c('pink', 'lightblue'))
text(bb, tab/2, labels=txt, cex=1)

Scatter Plot

This scatter demonstrates the correlation and proportionality between the two qualities, allowing us to establish whether or not turbidity and pH are connected.

with(water_potability, plot(Trihalomethanes, ph, col = Potability, pch = as.numeric(Potability)))

Remove Redundant Features:

This will find the correlation between the features and represent it in heat map

correlation_matrix <- cor(water_potability[,1:9])
high_correlation_features <- findCorrelation(correlation_matrix, cutoff = 0.5)
print(high_correlation_features)
integer(0)
heatmap(correlation_matrix)

we remove the correlation between the features and represent it in heat map

Feature selection:

Rank Features By Importance:

ranking features by importance is a technique used to identify the most influential variables in a dataset for predicting a target variable. This process helps in understanding which features have the most impact on the model’s performance. By ranking features by importance.

#train random forest model and calculate feature importance
rf = randomForest(x= water_potability[,1:9],y= water_potability[,10])
var_imp <- varImp(rf, scale = FALSE)
#sort the score in decreasing order
var_imp_df <- data.frame(cbind(variable = rownames(var_imp), score = var_imp[,1]))
var_imp_df$score <- as.double(var_imp_df$score)
var_imp_df[order(var_imp_df$score,decreasing = TRUE),]

ggplot(var_imp_df, aes(x=reorder(variable, score), y=score)) + 
  geom_point() +
  geom_segment(aes(x=variable,xend=variable,y=0,yend=score)) +
  ylab("IncNodePurity") +
  xlab("Variable Name") +
  coord_flip()

Recursive Feature elimination:

is a feature selection technique that recursively removes less important features from a model until the optimal subset is identified. It involves repeatedly training the model, ranking features based on their importance, and eliminating the least important ones.

 control <- rfeControl(functions=rfFuncs, method="cv",number=10)
 rf <- trainControl(method = "cv", number = 10, verboseIter = FALSE)
 # run the RFE algorithm
 rfe_model <- rfe(x= water_potability[,1:9],y= water_potability[,10], sizes=c(1:9), rfeControl=control)
 # summarize the results
 print(rfe_model)
 # list the chosen features
 predictors(rfe_model)
 # plot the results
plot(rfe_model, type=c("g", "o"))

Data transformation:

the process of converting or modifying raw data into a different format or structure to make it more suitable for analysis or modeling

Normlization

Normalization refers to the process of scaling variables to have a common range. It helps in comparing variables with different scales.

wp<- water_potability
normalize=function(x){return ((x-min(x))/(max(x)))}
wp$Solids=normalize(wp$Solids)

The solids attribute will create critical challenges because of the vast and diverted values: min is 320.9, and max is 43195.5, so we normalized the solids between 0 and 1 to make values smaller and more reasonable.

Discretization:

Discretization is the process of transforming continuous variables into discrete or categorical variables. It can be useful for analyzing data with many unique values or simplifying it.


wp$ph= cut(wp$ph, breaks = seq(3,11,by=4),right=FALSE)
wp$Hardness= cut(wp$Hardness, breaks = seq(120,280,by=40),right=FALSE)
wp$Chloramines = cut(wp$Chloramines, breaks = seq(3,11,by=4),right = FALSE)
wp$Sulfate= cut(wp$Sulfate, breaks = seq(220,440,by=44),right=FALSE)
wp$Conductivity= cut(wp$Conductivity, breaks = seq(200,700,by=100),right=FALSE)
wp$Organic_carbon= cut(wp$Organic_carbon, breaks = seq(4,24,by=4),right=FALSE)
wp$Trihalomethanes= cut(wp$Trihalomethanes, breaks = seq(20,110,by=10),right=FALSE)
wp$Turbidity= cut(wp$Turbidity, breaks = seq(1,7,by=2),right=FALSE)

print(wp)

Therefore, we transformed the continuous values of the numeric attributes into intervals by dividing the values to fall on one of the possible interval labels by discretization. The values will be meaningful and simpler to classify or perform other methods to help us later in our model. So, In Trihalomethanes, we intervals by dividing the values by 10 to have labels with equal width : [20,30) [30,40) [40,50) [50,60) [60,70) [70,80) [80,90) [90,100) [100,110).

##Encoding

encoding is the process of converting characters or strings into a specific encoding format. Since we don’t have a Nominal attribute in our database we couldn’t implement it.

wp

classfication and clustring In our dataset exploration, we employed both supervised and unsupervised learning methodologies through classification and clustering techniques.

For classification, we chose the decision tree algorithm, a recursive approach constructing a tree structure with leaf nodes signifying final decisions. The objective was to predict the class label (potability), with values 0 or 1, based on attributes like pH, Hardness, Solids, Chloramines, Sulfate, Conductivity, Organic_carbon, Trihalomethanes, and Turbidity. The dataset underwent division into training and testing sets for constructing and evaluating the decision tree. Model evaluation encompassed metrics like accuracy and cost-sensitive measures, gauged using a confusion matrix. Our toolkit included packages such as ‘party’ and ‘caret,’ incorporating methods like ‘sample’ for data splitting, ‘ctree’ for decision tree construction, ‘predict’ for testing predictions, and ‘confusionMatrix’ for model evaluation.

In the unsupervised clustering phase, we excluded the class label attribute “potability” and utilized numeric attributes such as pH, Hardness, Solids, Chloramines, Sulfate, Conductivity, Organic_carbon, Trihalomethanes, and Turbidity. Employing the K-means algorithm, clusters were formed, each represented by a center point, and objects were assigned to the nearest cluster. For this phase, we made use of packages such as ‘cluster’ and ‘factoextra,’ incorporating methods like ‘scale()’ for data scaling, ‘Kmeans()’ for cluster creation.

Cluster validation was performed using the ‘silhouette()’ method to calculate averages for each cluster. In both supervised and unsupervised techniques, we maintained result consistency by employing the ‘set.seed()’ method with the same random number when experimenting with different dataset sizes.

#Training technique

In the provided code, we systematically addressed outliers in multiple columns of our dataset. Beginning with a summary of each column’s statistics, including quartiles and the interquartile range (IQR), we established outlier detection limits. A visual assessment was conducted using boxplots, categorized by relevant variables such as “Potability.” A loop was implemented to iteratively identify and remove outliers in each column, ensuring a robust cleansing process. The final step involved summarizing the columns post-outlier removal, offering insights into the impact on the distribution of each variable. This comprehensive approach was applied uniformly to all dataset columns, promoting consistency in the outlier-handling process.

Information gain (ID3)

In this R code, the dataset undergoes a process of training and testing using the ID3 algorithm for decision tree classification. The data is successively split into training sets of 70%, 80%, and 90%, with corresponding testing sets of 30%, 20%, and 10%. The decision tree models are trained on these subsets, utilizing features such as pH, hardness, solids, chloramines, sulfate, conductivity, organic carbon, trihalomethanes, and turbidity to predict water potability.

For each split, the decision tree models are evaluated on their respective testing sets, and performance metrics such as confusion matrices and accuracy are computed. Additionally, Receiver Operating Characteristic (ROC) curves are generated, providing insights into the models’ discrimination capabilities. The Area Under the Curve (AUC) is calculated as a quantitative measure of model performance. This comprehensive approach enables a systematic exploration of the ID3 decision tree’s effectiveness in predicting water potability under varying training and testing scenarios.

Splitting the data set into two subsets: Training(70%) and Testing(30%):

set.seed(1958)
ind <- sample(2, nrow(wp), replace = TRUE, prob = c(0.7, 0.3))
train.data <- wp[ind == 1, ]
test.data <- wp[ind == 2, ]
train.data$Potability <- as.factor(train.data$Potability)
test.data$Potability <- as.factor(test.data$Potability)


myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate+Conductivity+Organic_carbon+Trihalomethanes+Turbidity
#myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate


m.ctree <- ctree(myFormula, data = train.data)
table(predict(m.ctree), train.data$Potability)

print(m.ctree)
plot(m.ctree, type="simple")

testPred <- predict(m.ctree, newdata = test.data)
result<-table(testPred, test.data$Potability)


co_result <- confusionMatrix(result)
print(co_result)
as.matrix(co_result, what = "classes")
acc <- co_result$overall["Accuracy"]
acc*100


pred_probs <- as.numeric(predict(m.ctree, newdata = test.data, type = "response"))
binary_outcome <- as.numeric(test.data$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(80%) and Testing(20%):


set.seed(1958)
ind <- sample(2, nrow(wp), replace = TRUE, prob = c(0.8, 0.2))
train.data <- wp[ind == 1, ]
test.data <- wp[ind == 2, ]
train.data$Potability <- as.factor(train.data$Potability)

myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate+Conductivity+Organic_carbon+Trihalomethanes+Turbidity
#myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate


m.ctree <- ctree(myFormula, data = train.data)
table(predict(m.ctree), train.data$Potability)

print(m.ctree)
plot(m.ctree, type="simple")

testPred <- predict(m.ctree, newdata = test.data)
result<-table(testPred, test.data$Potability)


co_result <- confusionMatrix(result)
print(co_result)
as.matrix(co_result, what = "classes")
acc <- co_result$overall["Accuracy"]
acc*100

pred_probs <- as.numeric(predict(m.ctree, newdata = test.data, type = "response"))
binary_outcome <- as.numeric(test.data$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(90%) and Testing(10%):

set.seed(1958)
ind <- sample(2, nrow(wp), replace = TRUE, prob = c(0.9, 0.1))
train.data <- wp[ind == 1, ]
test.data <- wp[ind == 2, ]
train.data$Potability <- as.factor(train.data$Potability)

myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate+Conductivity+Organic_carbon+Trihalomethanes+Turbidity
#myFormula <- Potability ~ ph+Hardness+Solids+Chloramines+Sulfate


m.ctree <- ctree(myFormula, data = train.data)
table(predict(m.ctree), train.data$Potability)

print(m.ctree)
plot(m.ctree, type="simple")

testPred <- predict(m.ctree, newdata = test.data)
result<-table(testPred, test.data$Potability)


co_result <- confusionMatrix(result)
print(co_result)
as.matrix(co_result, what = "classes")
acc <- co_result$overall["Accuracy"]
acc*100

pred_probs <- as.numeric(predict(m.ctree, newdata = test.data, type = "response"))
binary_outcome <- as.numeric(test.data$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Gain ratio (C4.5)

a decision tree-based classifier, is utilized for model training and evaluation. The dataset undergoes cross-validation with different fold settings (3 folds, 5 folds, and 10 folds). For each fold configuration, a J48 model is trained, and its predictive performance is assessed using Receiver Operating Characteristic (ROC) curves. The Area Under the Curve is calculated for each ROC curve, providing a quantitative measure of the model’s ability to predict water potability. Notably, the visual inspection of the ROC curves indicates that the model trained with 10-fold cross-validation exhibits the highest discriminative performance. This comparative analysis across various cross-validation scenarios offers valuable insights into the robustness and generalization capability.

# 3 folds
set.seed(1958)
train <- createFolds(wp$Potability, k=3)
C45Fit <- train(Potability ~ .,method = "J48",data = wp,
                trControl = trainControl(
                method = "cv",
                index = train,
                savePredictions = TRUE))

C45Fit

C45Fit$finalModel

pred_probs <- predict(C45Fit, newdata = wp, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(wp$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")
# 5 folds
set.seed(1958)
train <- createFolds(wp$Potability, k=5)
C45Fit <- train(Potability ~., method="J48", data=wp,
                trControl = trainControl(
                method ="cv", 
                index = train,
                savePredictions = TRUE))

C45Fit

C45Fit$finalModel

pred_probs <- predict(C45Fit, newdata = wp, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(wp$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")
# 10 folds
set.seed(1958)
train <- createFolds(wp$Potability, k=10)
C45Fit <- train(Potability ~., method="J48", data=wp,
                trControl = trainControl(
                  method="cv", indexOut=train))

C45Fit

C45Fit$finalModel

pred_probs <- predict(C45Fit, newdata = wp, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(wp$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

C5.0 newer version of C4.5 Splitting the data set into two subsets: Training(70%) and Testing(30%):

set.seed(1958)
train.indices <- sample(2, nrow(water_potability), replace=TRUE, prob=c(0.7, 0.3))
w.train <- water_potability[train.indices == 1, ]
w.test <- water_potability[train.indices == 2, ]
w.train$Potability <- as.factor(w.train$Potability)

model <- C5.0(Potability ~., data=w.train)

results <- predict(object=model, newdata=w.test, type="class")

table(results, w.test$Potability)

plot(model)

r <- confusionMatrix(results, w.test$Potability)
acc <- r$overall["Accuracy"]*100
acc
as.matrix(r, what = "classes")
print(r)



pred_probs <- predict(model, newdata = w.test, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(w.test$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(80%) and Testing(20%):

set.seed(1958)
train.indices <- sample(2, nrow(water_potability), replace=TRUE, prob=c(0.8, 0.2))
w.train <- water_potability[train.indices == 1, ]
w.test <- water_potability[train.indices == 2, ]
w.train$Potability <- as.factor(w.train$Potability)


model <- C5.0(Potability ~., data=w.train)

results <- predict(object=model, newdata=w.test, type="class")

table(results, w.test$Potability)

plot(model)

r <- confusionMatrix(results, w.test$Potability)
acc <- r$overall["Accuracy"]*100
acc
as.matrix(r, what = "classes")
print(r)


pred_probs <- predict(model, newdata = w.test, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(w.test$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(90%) and Testing(10%):

set.seed(1958)
train.indices <- sample(2, nrow(water_potability), replace=TRUE, prob=c(0.9, 0.1))
w.train <- water_potability[train.indices == 1, ]
w.test <- water_potability[train.indices == 2, ]
w.train$Potability <- as.factor(w.train$Potability)


model <- C5.0(Potability ~., data=w.train)

results <- predict(object=model, newdata=w.test, type="class")

table(results, w.test$Potability)

plot(model)

###To improve the readability of the decision tree, we decided to sample the data using only the pH and sulfate attributes. We then split the data into training and testing sets using the same split points: ###Training(90%) and Testing(10%), which allowed for a more manageable decision tree:

set.seed(1958)
importent_feature_sample <- select(water_potability,c(1,5,10))
train.indices <- sample(2, nrow(importent_feature_sample), replace=TRUE, prob=c(0.9, 0.1))
w.train <- importent_feature_sample[train.indices == 1, ]
w.test <- importent_feature_sample[train.indices == 2, ]
w.train$Potability <- as.factor(w.train$Potability)


model <- C5.0(Potability ~., data=w.train)

results <- predict(object=model, newdata=w.test, type="class")

table(results, w.test$Potability)

plot(model)

r <- confusionMatrix(results, w.test$Potability)
acc <- r$overall["Accuracy"]*100
acc
as.matrix(r, what = "classes")
print(r)



pred_probs <- predict(model, newdata = w.test, type = "prob")[, "Potable"]
binary_outcome <- as.numeric(w.test$Potability == "Potable")
# ROC curve
roc_curve <- roc(binary_outcome, pred_probs)
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

###Gini index (CART) we employed the C5.0 algorithm, an enhanced version of the C4.5 decision tree, for model training and evaluation across different training and testing set splits. The dataset underwent three scenarios: Training(70%) and Testing(30%), Training(80%) and Testing(20%), and Training(90%) and Testing(10%). For each case, the C5.0 model was trained on the designated training data, evaluated on the testing data, and its performance was assessed through accuracy, confusion matrix, and ROC curve with Area Under the Curve (AUC).

Upon comparative analysis, it was observed that the model trained with a larger proportion of data (Training 90%, Testing 10%) demonstrated superior performance, achieving higher accuracy and a more discriminative ROC curve. This exploration across different training and testing splits provides valuable insights into the robustness and generalization capability of the C5.0 decision tree algorithm for predicting water potability.

Splitting the data set into two subsets: Training(70%) and Testing(30%):

set.seed(1958)
train = sample(2, nrow(wp), replace=TRUE, prob=c(0.7, 0.3))
wp.train=wp[train == 1,]
wp.test=wp[train == 2,]



fit.tree = rpart(Potability ~ ., data=wp, method = "class", cp=0.008)
fit.tree

rpart.plot(fit.tree)

fit.tree$variable.importance

pred.tree = predict(fit.tree, wp.test, type = "class")
re <- table(pred.tree, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100

plotcp(fit.tree)
printcp(fit.tree)

# Explicitly request the lowest cp value
fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]

bestcp <-fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]
pruned.tree <- prune(fit.tree, cp = bestcp)
rpart.plot(pruned.tree)

pred.prune = predict(pruned.tree, wp.test, type="class")

re <- table(pred.prune, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100



pred.tree_raw <- predict(fit.tree, wp.test)
# Convert to probabilities
pred.tree_probs <- exp(pred.tree_raw) / (1 + exp(pred.tree_raw))
# Extract probabilities for the "Potable" class
roc_curve <- roc(ifelse(wp.test$Potability == "Potable", 1, 0), pred.tree_probs[, "Potable"])
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(80%) and Testing(20%):

set.seed(1958)
train = sample(2, nrow(wp), replace=TRUE, prob=c(0.8, 0.2))
wp.train=wp[train == 1,]
wp.test=wp[train == 2,]



fit.tree = rpart(Potability ~ ., data=wp.train, method = "class", cp=0.008)
fit.tree

rpart.plot(fit.tree)

fit.tree$variable.importance

pred.tree = predict(fit.tree, wp.test, type = "class")
re <- table(pred.tree, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100

plotcp(fit.tree)
printcp(fit.tree)

# Explicitly request the lowest cp value
fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]

bestcp <-fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]
pruned.tree <- prune(fit.tree, cp = bestcp)
rpart.plot(pruned.tree)

pred.prune = predict(pruned.tree, wp.test, type="class")

re <- table(pred.prune, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100


pred.tree_raw <- predict(fit.tree, wp.test)
pred.tree_probs <- exp(pred.tree_raw) / (1 + exp(pred.tree_raw))
roc_curve <- roc(ifelse(wp.test$Potability == "Potable", 1, 0), pred.tree_probs[, "Potable"])
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Splitting the data set into two subsets: Training(90%) and Testing(10%):

set.seed(1958)
train = sample(2, nrow(wp), replace=TRUE, prob=c(0.9, 0.1))
wp.train=wp[train == 1,]
wp.test=wp[train == 2,]



fit.tree = rpart(Potability ~ ., data=wp.train, method = "class", cp=0.008)
fit.tree

rpart.plot(fit.tree)

fit.tree$variable.importance

pred.tree = predict(fit.tree, wp.test, type = "class")
re <- table(pred.tree, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100

plotcp(fit.tree)
printcp(fit.tree)

# Explicitly request the lowest cp value
fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]

bestcp <-fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"]
pruned.tree <- prune(fit.tree, cp = bestcp)
rpart.plot(pruned.tree)

pred.prune = predict(pruned.tree, wp.test, type="class")

re <- table(pred.prune, wp.test$Potability)

co_re <- confusionMatrix(re)
print(co_re)
as.matrix(co_re, what = "classes")
acc <- co_re$overall["Accuracy"]
acc*100



pred.tree_raw <- predict(fit.tree, wp.test)
pred.tree_probs <- exp(pred.tree_raw) / (1 + exp(pred.tree_raw))
roc_curve <- roc(ifelse(wp.test$Potability == "Potable", 1, 0), pred.tree_probs[, "Potable"])
plot(roc_curve, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, col = "gray", lty = 2)
# Print AUC
cat("AUC:", auc(roc_curve), "\n")

Comparison Criteria:

Information Gain: Gain Ratio: Gini index:
Accuracy 0.5820611 0.9686781 0.5935115
precision 0.58546169 0.005417132 0.6004320
sensitivity 0.97385621 0.9334741 0.9084967
specificity 0.03211009 0.880677 0.1513761

As shown in the comparison the best technique to choose is Gain Raio Due to the high accuracy

summary(water_potability)
str(water_potability)

Clustering

determine and visualize optimal number of clusters:

we will use four different sizes of k for clustering and then see what performs best between them.

Scale data first:

Confirm that all the columns you are trying to scale are indeed numeric. You can use sapply() to check and coerce them to numeric if necessary.

sinec all coulme are numeric we wll scale all of them expet class label and we saved it in dataset called Cluster and we used it in Clustring

water_potability<- sapply(water_potability, as.numeric)

data_for_cluster <- scale(water_potability[, !colnames(water_potability) %in% "Potability"])
#we use !colnames(water_potability) %in% "Potability" to exclude the "Potability" column
View(data_for_cluster)

Clustring1

K-means

# 3- run k-means clustering to find 2 clusters
#set a seed for random number generation  to make the results reproducible
set.seed(8953)
kmeans.result <- kmeans(data_for_cluster,2)
# print the clusterng result
kmeans.result

visualize clustering

# visualize clustering (2 clusters)
fviz_cluster(kmeans.result, data = data_for_cluster)

hiercrchical clustering

we took 50 sample to make it more understabale

# draw a sample of 50 records from the data, so that the clustering plot will not be over crowded and easy to undrestand 
idx<-sample(1:dim(data_for_cluster)[1], 50)
sample_c1<-data_for_cluster[idx, ]

## hiercrchical clustering
hc.cut<- hcut(sample_c1, k = 2, hc_method= "complete")

Visualize dendrogram and sample Clustering

dendrogram is a tree diagram that displays the arrangement of data points in a hierarchical order based on their similarity or dissimilarity.

# Visualize dendrogram
fviz_dend(hc.cut,rect= TRUE)
# Visualize cluster
fviz_cluster(hc.cut, ellipse.type= "convex")

average silhouette

This method calculates the average silhouette width for different values of k, determining how well data points fit into their assigned clusters.

 #average silhouette for each clusters 

avg_sil <- silhouette(kmeans.result$cluster,dist(data_for_cluster)) #a dissimilarity object inheriting from class dist or coercible to one. If not specified, dmatrix must be.
fviz_silhouette(avg_sil)#k-means clustering with estimating k and initializations

BCubed precision and recall

BCubed precision and recall are metrics used to evaluate the performance of clustering algorithms, particularly in the context of evaluating the quality of clustering assignments for individual data points.


cluster_assignments <- c(kmeans.result$cluster)
ground_truth_labels <- c(water_potability)
data <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Function to calculate BCubed precision and recall
calculate_bcubed_metrics <- function(data) {
  n <- nrow(data)
  precision_sum <- 0
  recall_sum <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
same_category_same_cluster <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
precision_sum <- precision_sum + same_category_same_cluster /total_same_cluster
recall_sum <- recall_sum + same_category_same_cluster / total_same_category
  }

  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
cat("BCubed Recall:", recall, "\n")

Clustring2

K-means

run k-means clustering to find 3 clusters set a seed for random number generation to make the results reproducible

set.seed(8953)
kmeans.result <- kmeans(data_for_cluster,3)
# print the Clustring result
kmeans.result

visualize clustering

# visualize clustering (3 clusters)

fviz_cluster(kmeans.result, data = data_for_cluster)

hiercrchical clustering

# draw a sample of 50 records from the data, so that the clustering plot will not be over crowded and easy to undrestand 

idx2<-sample(1:dim(data_for_cluster)[1], 50)
sample_c2<-data_for_cluster[idx2, ]

## hiercrchical clustering
hc2.cut<- hcut(sample_c2, k = 3, hc_method= "complete")

Visualize dendrogram

dendrogram is a tree diagram that displays the arrangement of data points in a hierarchical order based on their similarity or dissimilarity.

fviz_dend(hc2.cut,rect= TRUE)
# Visualize cluster
fviz_cluster(hc2.cut, ellipse.type= "convex")

average silhouette

This method calculates the average silhouette width for different values of k, determining how well data points fit into their assigned clusters.

 #average silhouette for each clusters 

avg_sil <- silhouette(kmeans.result$cluster,dist(data_for_cluster)) #a dissimilarity object inheriting from class dist or coercible to one. If not specified, dmatrix must be.
fviz_silhouette(avg_sil)#k-means clustering with estimating k and initializations

BCubed precision and recall

BCubed precision and recall are metrics used to evaluate the performance of clustering algorithms, particularly in the context of evaluating the quality of clustering assignments for individual data points.


cluster_assignments <- c(kmeans.result$cluster)
ground_truth_labels <- c(water_potability)
data <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Function to calculate BCubed precision and recall
calculate_bcubed_metrics <- function(data) {
  n <- nrow(data)
  precision_sum <- 0
  recall_sum <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
same_category_same_cluster <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
precision_sum <- precision_sum + same_category_same_cluster /total_same_cluster
recall_sum <- recall_sum + same_category_same_cluster / total_same_category
  }

  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
cat("BCubed Recall:", recall, "\n")

Clustring3

K-means

# 3- run k-means clustering to find 4 clusters
#set a seed for random number generation  to make the results reproducible
set.seed(8953)
kmeans.result <- kmeans(data_for_cluster,4)
# print the clustering result
kmeans.result

visualize clustering

# visualize clustering (4 clusters)

fviz_cluster(kmeans.result, data = data_for_cluster)

hiercrchical clustering

# draw a sample of 50 records from the data, so that the clustering plot will not be over crowded and easy to undrestand 
idx3<-sample(1:dim(data_for_cluster)[1], 50)
sample_c3<-data_for_cluster[idx3, ]

## hiercrchicalclustering
hc3.cut<- hcut(sample_c3, k = 4, hc_method= "complete")

#dendrogram

dendrogram is a tree diagram that displays the arrangement of data points in a hierarchical order based on their similarity or dissimilarity.

# Visualize dendrogram
fviz_dend(hc3.cut,rect= TRUE)
# Visualize cluster
fviz_cluster(hc3.cut, ellipse.type= "convex")

#average silhouette

This method calculates the average silhouette width for different values of k, determining how well data points fit into their assigned clusters.

 #average silhouette for each clusters 

avg_sil <- silhouette(kmeans.result$cluster,dist(data_for_cluster)) #a dissimilarity object inheriting from class dist or coercible to one. If not specified, dmatrix must be.
fviz_silhouette(avg_sil)#k-means clustering with estimating k and initializations

BCubed precision and recall

BCubed precision and recall are metrics used to evaluate the performance of clustering algorithms, particularly in the context of evaluating the quality of clustering assignments for individual data points.


cluster_assignments <- c(kmeans.result$cluster)
ground_truth_labels <- c(water_potability)
data <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Function to calculate BCubed precision and recall
calculate_bcubed_metrics <- function(data) {
  n <- nrow(data)
  precision_sum <- 0
  recall_sum <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
same_category_same_cluster <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
precision_sum <- precision_sum + same_category_same_cluster /total_same_cluster
recall_sum <- recall_sum + same_category_same_cluster / total_same_category
  }

  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
cat("BCubed Recall:", recall, "\n")

Clustring4

K-means run k-means clustering to find 5 clusters

#set a seed for random number generation  to make the results reproducible
set.seed(8953)
kmeans.result <- kmeans(data_for_cluster,5)
# print the clusterng result
kmeans.result

visualize clustering

# visualize clustering (5 clusters)

fviz_cluster(kmeans.result, data = data_for_cluster)

hiercrchical clustering

# draw a sample of 50 records from the data, so that the clustering plot will not be over crowded and easy to undrestand 
idx4<-sample(1:dim(data_for_cluster)[1], 50)
sample_c4<-data_for_cluster[idx4, ]

## hiercrchicalclustering
hc4.cut<- hcut(sample_c4, k = 5, hc_method= "complete")

dendrogram

dendrogram is a tree diagram that displays the arrangement of data points in a hierarchical order based on their similarity or dissimilarity.

# Visualize dendrogram
fviz_dend(hc4.cut,rect= TRUE)
# Visualize cluster
fviz_cluster(hc4.cut, ellipse.type= "convex")

#average silhouette

This method calculates the average silhouette width for different values of k, determining how well data points fit into their assigned clusters. determining how well data points fit into their assigned clusters.

 #average silhouette for each clusters 

avg_sil <- silhouette(kmeans.result$cluster,dist(data_for_cluster)) #a dissimilarity object inheriting from class dist or coercible to one. If not specified, dmatrix must be.
fviz_silhouette(avg_sil)#k-means clustering with estimating k and initializations

BCubed precision and recall

BCubed precision and recall are metrics used to evaluate the performance of clustering algorithms, particularly in the context of evaluating the quality of clustering assignments for individual data points.


cluster_assignments <- c(kmeans.result$cluster)
ground_truth_labels <- c(water_potability)
data <- data.frame(cluster = cluster_assignments, label = ground_truth_labels)

# Function to calculate BCubed precision and recall
calculate_bcubed_metrics <- function(data) {
  n <- nrow(data)
  precision_sum <- 0
  recall_sum <- 0

  for (i in 1:n) {
    cluster <- data$cluster[i]
    label <- data$label[i]
    
# Count the number of items from the same category within the same cluster
same_category_same_cluster <- sum(data$label[data$cluster == cluster] == label)
    
# Count the total number of items in the same cluster
total_same_cluster <- sum(data$cluster == cluster)
    
# Count the total number of items with the same category
total_same_category <- sum(data$label == label)
    
# Calculate precision and recall for the current item and add them to the sums
precision_sum <- precision_sum + same_category_same_cluster /total_same_cluster
recall_sum <- recall_sum + same_category_same_cluster / total_same_category
  }

  # Calculate average precision and recall
  precision <- precision_sum / n
  recall <- recall_sum / n

  return(list(precision = precision, recall = recall))
}

# Calculate BCubed precision and recall
metrics <- calculate_bcubed_metrics(data)

# Extract precision and recall from the metrics
precision <- metrics$precision
recall <- metrics$recall

# Print the results
cat("BCubed Precision:", precision, "\n")
cat("BCubed Recall:", recall, "\n")

Elbow method

elbow method helps find the optimal number of clusters (k) in k-means clustering. the “elbow” point is where the rate of decrease in WSS slows, indicating a good balance between cluster count and cluster compactness. The goal is to select the smallest k that retains most of the data’s variability.

# 3- Elbow method
#fviz_nbclust() with within cluster sums of squares (wss) method
 
fviz_nbclust(data_for_cluster, kmeans, method = "wss") +
  geom_vline(xintercept = 4, linetype = 2)+
  labs(subtitle = "Elbow method")

Total Within-Cluster Sum of Squares for k

WSS is to give you an indication of how well the data can be represented by a certain number of clusters. In k-means clustering, typically choose the number of clusters (k) that minimizes this total WSS.


for (k in 2:5) {
  kmeans_result <- kmeans(water_potability, centers = k)
  total_withinss <- kmeans_result$tot.withinss
  cat("Total Within-Cluster Sum of Squares for k =", k, ":", total_withinss, "\n")
}

Comparison Criteria:

Clustring1 Clustring2 Clustring3 Clustring4
average silhouette width 0.08 0.08 0.07 0.08
BCubed Precision 0.0053507 0.005417132 0.005466656 0.005525278
BCubed Recall 0.9501209 0.9334741 0.9250736 0.9201568

Total Within-Cluster Sum of Squares for k = 2 : 35435736178

Total Within-Cluster Sum of Squares for k = 3 : 18005636207

Total Within-Cluster Sum of Squares for k = 4 : 10644686913

Total Within-Cluster Sum of Squares for k = 5 : 7263150876

LS0tCnRpdGxlOiAiV2F0ZXIgUXVhbGl0eSBhbmQgUG90YWJpbGl0eSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgVGhpcyBjb2RlIGlzIHJ1bm5pbmcgdXNpbmcgUiBub3RlYm9vayBpbiBSU3R1ZGlvCgojIyMgR29hbHM6CgpXZSBhcmUgY29sbGVjdGluZyBhIGRhdGFzZXQgb24gd2F0ZXIgcXVhbGl0eSB0byB0cmFpbiBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgZm9yIGJpbmFyeSBjbGFzc2lmaWNhdGlvbjogZGV0ZXJtaW5pbmcgd2hldGhlciB3YXRlciBpcyBzYWZlIGZvciBjb25zdW1wdGlvbiAoMSkgb3Igbm90ICgwKS4gVGhpcyBtb2RlbCB3aWxsIGhlbHAgd2l0aCB3YXRlciB0cmVhdG1lbnQgZGVjaXNpb25zIGFuZCBlbnN1cmUgY29tcGxpYW5jZSB3aXRoIHF1YWxpdHkgc3RhbmRhcmRzLiBXZSBhcHBsaWVkIGRpZmZlcmVudCBTdW1tYXJ6aXRpb24gYW5kIHBsb3R0aW5nIG1ldGhvZHMgdG8gaGVscCB1cyB0byB1bmRlcnN0YW5kIG91ciBkYXRhc2V0LCBzdWNoIGFzIHNjYXR0ZXIsIGhpc3RvZ3JhbSBhbmQgYmFyIHBsb3QuIFRoZW4sIHdlIGFwcGx5ZWQgcHJlcHJvY2VzcyBpbiBvdXIgZGF0YSB1c2luZyBkYXRhIGNsZWFuaW5nLCBkYXRhIHRyYW5zZm9ybWF0aW9uIGFuZCBmZWF0dXJlIHNlbGVjdGlvbi4KCiMjIyBDbGFzc2lmaWNhdGlvbiBhbmQgQ2x1c3RlcmluZyBHb2FsOgoKMS1jbGFzc2lmaWNhdGlvbiBpbiB0aGlzIGRhdGFzZXQgaXMgdG8gYnVpbGQgYSBwcmVkaWN0aXZlIG1vZGVsIHRoYXQgY2FuIGNsYXNzaWZ5IHdhdGVyIHNhbXBsZXMgaW50byB0d28gY2F0ZWdvcmllczogcG90YWJsZSAoc3VpdGFibGUgZm9yIGNvbnN1bXB0aW9uKSBvciBub24tcG90YWJsZSAobm90IHN1aXRhYmxlIGZvciBjb25zdW1wdGlvbikuCgoyLWNsdXN0ZXJpbmcgaW4gdGhpcyBkYXRhc2V0IGlzIHRvIGlkZW50aWZ5IG5hdHVyYWwgZ3JvdXBpbmdzIG9yIGNsdXN0ZXJzIHdpdGhpbiB0aGUgd2F0ZXIgc2FtcGxlcyBiYXNlZCBvbiB0aGVpciBxdWFsaXR5IHBhcmFtZXRlcnMuCgojIyMgU291cmNlIG9mIHRoZSBkYXRhc2V0OgoKS2FnZ2xlCgojIyMgbGluayBvZiB0aGUgZGF0YXNldDoKCjxodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL3VvbTE5MDM0NmEvd2F0ZXItcXVhbGl0eS1hbmQtcG90YWJpbGl0eT4KCgojIGxpYnJhcnk6CmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQojaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikKI2luc3RhbGwucGFja2FnZXMoIkJvcnV0YSIpCiNpbnN0YWxsLnBhY2thZ2VzKCJtbGJlbmNoIikKI2luc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCmxpYnJhcnkoY2x1c3RlcikKbGlicmFyeShmYWN0b2V4dHJhKSAKbGlicmFyeShvdXRsaWVycykKbGlicmFyeShkcGx5cikKbGlicmFyeShtbGJlbmNoKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShCb3J1dGEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkocFJPQykKbGlicmFyeShlMTA3MSkKbGlicmFyeShjYXJldCkKbGlicmFyeShwYXJ0eSkKbGlicmFyeShwYXJ0eWtpdCkKbGlicmFyeShSV2VrYSkKbGlicmFyeShDNTApCmxpYnJhcnkocHJpbnRyKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCgpnZXR3ZCgpCiNzZXR3ZCgiL1VzZXJzL21haGF5aWUvRGVza3RvcC8zMjZwIikKI2dldHdkKCkKCndhdGVyX3BvdGFiaWxpdHkgPSByZWFkLmNzdignRGF0YXNldC93YXRlcl9wb3RhYmlsaXR5LmNzdicpCgpzdHIod2F0ZXJfcG90YWJpbGl0eSkKYGBgCgoKIyMjIHNhbXBsZSBvZiBkYXRhCgpzYW1wbGUgb2YgcmF3IGRhdGFzZXQoZmlyc3QgMTAgcm93cyk6CmBgYHtyfQoKaGVhZCh3YXRlcl9wb3RhYmlsaXR5LDEwKQpgYGAKCnNhbXBsZSBvZiByYXcgZGF0YXNldChsYXN0IDEwIHJvd3MpOgpgYGB7cn0KdGFpbCh3YXRlcl9wb3RhYmlsaXR5LCAxMCkKYGBgCgpGaXZlIG51bWJlciBzdW1tYXJ5IG9mIGVhY2ggYXR0cmlidXRlIGluIG91ciBkYXRhc2V0OgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5KQpgYGAKCiMjIyBOdW1iZXIgb2YgY29sdW1uIGFuZCByb3dzCgpgYGB7cn0KZGltKHdhdGVyX3BvdGFiaWxpdHkpCmBgYAoKIyMjIyBTYW1wbGUgb2YgV2F0ZXJfcG90YWJpbGl0eSBkYXRhc2V0CgpUaGlzIGlzIGEgc2FtcGxlIG9mIHRoZSBkYXRhc2V0IHRvIGhlbHAgdG8gdW5kZXJzdGFuZCBob3cgaXQgaXMgc3RydWN0dXJlZCBhbmQgb3JnYW5pemVkCgpgYGB7cn0KVmlldyh3YXRlcl9wb3RhYmlsaXR5KQpzYW1wbGUod2F0ZXJfcG90YWJpbGl0eSkKYGBgCgoKIyMjIENoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlczoKClRoZSBhYnNlbmNlIG9mIGRhdGEgaW4gY2VydGFpbiB2YXJpYWJsZXMgb3IgY29sdW1ucyBpbiBhIGRhdGFzZXQgaXMgcmVmZXJyZWQgdG8gYXMgbWlzc2luZyBvciBudWxsIHZhbHVlcyBkdWUgdG8gdmFyaW91cyByZWFzb25zLiBJdCBjYW4gaGF2ZSBhIG5lZ2F0aXZlIGltcGFjdCBvbiB0aGUgZGF0YXNldCdzIGVmZmljaWVuY3kgYW5kIHRoZSBpbmZvcm1hdGlvbiB0aGF0IGNhbiBiZSB0YWtlbiBmcm9tIGl0IGxhdGVyLCBzbyB3ZSBjaGVja2VkIHRvIHNlZSB3aGV0aGVyIG91ciBkYXRhIGhhZCBtaXNzaW5nIG9yIG51bGwgdmFsdWVzIGFuZCBlbGltaW5hdGVkIHRoZXNlIHJvd3MgdG8gcHJvZHVjZSBhIG1vcmUgZWZmaWNpZW50IGRhdGFzZXQuCgpmaXJzdCB3ZSBjaGVja2VkIGZvciBtaXNzaW5nIHZhbHVlIHRvIGVuc3VyZXMgYWNjdXJhdGUgc3RhdGlzdGljcywgcmVsaWFibGUgdmlzdWFsaXphdGlvbnMsIGFuZCBndWlkZXMgZGVjaXNpb25zIG9uIGltcHV0YXRpb24gb3IgcmVtb3ZhbCBvZiBtaXNzaW5nIGRhdGEuCgpgYGB7cn0KZGltKHdhdGVyX3BvdGFiaWxpdHkpCnN1bShpcy5uYSh3YXRlcl9wb3RhYmlsaXR5KSkKYGBgCgojIyMgUmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcwoKYGBge3J9CmNvbFN1bXMoaXMubmEod2F0ZXJfcG90YWJpbGl0eSkpCndhdGVyX3BvdGFiaWxpdHkgPSBuYS5vbWl0KHdhdGVyX3BvdGFiaWxpdHkpCmBgYAoKCmBgYHtyfQpjb2xTdW1zKGlzLm5hKHdhdGVyX3BvdGFiaWxpdHkpKQpWaWV3KHdhdGVyX3BvdGFiaWxpdHkpCmBgYAoKIyMjIHNlY29uZCByZW1vdmUgcm93cwoKIyMjIFN0YW5kYXJkIGRldmlhdGlvbjoKClRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaW4gc3RhdGlzdGljcyBpcyBhIG1lYXN1cmUgdXNlZCB0byBhc3Nlc3MgdGhlIHNwcmVhZCBvZiBkYXRhIGFyb3VuZCB0aGUgbWVhbi4gSXQgZ2l2ZXMgdXMgYW4gaWRlYSBvZiBob3cgbXVjaCB0aGUgZGF0YSBwb2ludHMgZGV2aWF0ZSBmcm9tIHRoZSBhdmVyYWdlLgoKYGBge3J9CnNkKHdhdGVyX3BvdGFiaWxpdHkkVHVyYmlkaXR5KQpzZCh3YXRlcl9wb3RhYmlsaXR5JFNvbGlkcykKc2Qod2F0ZXJfcG90YWJpbGl0eSRDb25kdWN0aXZpdHkpCnNkKHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCnNkKHdhdGVyX3BvdGFiaWxpdHkkcGgpCmBgYAoKd2UgdWVzIGl0IGZvciBmaXZlIGNvdWxtZSBUdXJiaWRpdHksU29saWRzLENvbmR1Y3Rpdml0eSxPcmdhbmljX2NhcmJvbixwaC4KCiMjIyBNZWFuOgoKdGhlIGF2ZXJhZ2UsIGlzIGEgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5IGluIHN0YXRpc3RpY3MuIEl0IGlzIGNhbGN1bGF0ZWQgYnkgc3VtbWluZyB1cCBhbGwgdGhlIHZhbHVlcyBpbiBhIGRhdGFzZXQgYW5kIGRpdmlkaW5nIGJ5IHRoZSBudW1iZXIgb2YgdmFsdWVzLiBUaGUgbWVhbiBnaXZlcyB1cyBhIHJlcHJlc2VudGF0aXZlIHZhbHVlIHRoYXQgaXMgdHlwaWNhbGx5IHVzZWQgdG8gZGVzY3JpYmUgdGhlICJ0eXBpY2FsIiB2YWx1ZSBpbiBhIHNldCBvZiBkYXRhLgoKYGBge3J9Cm1lYW4od2F0ZXJfcG90YWJpbGl0eSRUdXJiaWRpdHkpCm1lYW4od2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMpIAptZWFuKHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5KSAKbWVhbih3YXRlcl9wb3RhYmlsaXR5JE9yZ2FuaWNfY2FyYm9uKSAKbWVhbih3YXRlcl9wb3RhYmlsaXR5JHBoKSAKYGBgCgp3ZSB1ZXMgaXQgZm9yIGZpdmUgY291bG1lIFR1cmJpZGl0eSxTb2xpZHMsQ29uZHVjdGl2aXR5LE9yZ2FuaWNfY2FyYm9uLHBoLgoKIyMjIE1lZGlhbjoKCkl0IHJlcHJlc2VudHMgdGhlIG1pZGRsZSB2YWx1ZSBpbiBhIGRhdGFzZXQgd2hlbiB0aGUgdmFsdWVzIGFyZSBhcnJhbmdlZCBpbiBhc2NlbmRpbmcgb3IgZGVzY2VuZGluZyBvcmRlci4KCmBgYHtyfQptZWRpYW4od2F0ZXJfcG90YWJpbGl0eSRUdXJiaWRpdHkpCm1lZGlhbih3YXRlcl9wb3RhYmlsaXR5JFNvbGlkcykKbWVkaWFuKHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5KQptZWRpYW4od2F0ZXJfcG90YWJpbGl0eSRPcmdhbmljX2NhcmJvbikKbWVkaWFuKHdhdGVyX3BvdGFiaWxpdHkkcGgpCmBgYAoKd2UgdWVzIGl0IGZvciBmaXZlIGNvdWxtZSBUdXJiaWRpdHksU29saWRzLENvbmR1Y3Rpdml0eSxPcmdhbmljX2NhcmJvbixwaC4KCiMjIyBWYXJpYW5jZToKCkl0IHByb3ZpZGVzIGluZm9ybWF0aW9uIGFib3V0IGhvdyBmYXIgZWFjaCB2YWx1ZSBpbiB0aGUgZGF0YXNldCBpcyBmcm9tIHRoZSBtZWFuLiBBIGhpZ2hlciB2YXJpYW5jZSBpbmRpY2F0ZXMgYSBncmVhdGVyIHNwcmVhZCBvZiBkYXRhLCB3aGlsZSBhIGxvd2VyIHZhcmlhbmNlIHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGEgcG9pbnRzIGFyZSBjbG9zZXIgdG8gdGhlIG1lYW4uCgpgYGB7cn0KdmFyKHdhdGVyX3BvdGFiaWxpdHkkVHVyYmlkaXR5KQp2YXIod2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMpCnZhcih3YXRlcl9wb3RhYmlsaXR5JENvbmR1Y3Rpdml0eSkKdmFyKHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCnZhcih3YXRlcl9wb3RhYmlsaXR5JHBoKQpgYGAKCkFzIHlvdSBjYW4gc2VlIHRoZSBoaWdoZXN0IFZhcmlhbmNlIFNvbGlkcywgYW5kIHRoZSBsb3dlc3QgVHVyYmlkaXR5LgoKIyMjIFN0YXRpc3RpY2FsIE1lYXN1cmVzOgoKIyMjIEluIHRoZSBnaXZlbiBjb2RlLCB5b3UndmUgdXNlZCB0aGUgc3VtbWFyeSBmdW5jdGlvbiB0byBvYnRhaW4ga2V5IHN0YXRpc3RpY2FsIG1lYXN1cmVzIGZvciB2YXJpYWJsZXMgc3VjaCBhcyAiQ29uZHVjdGl2aXR5LCIgIk9yZ2FuaWNfY2FyYm9uLCIgIkhhcmRuZXNzLCIgZXRjLiwgaW4gdGhlIHdhdGVyX3BvdGFiaWxpdHkgZGF0YXNldC4gVGhlc2UgbWVhc3VyZXMgaW5jbHVkZSBtaW5pbXVtLCAxc3QgcXVhcnRpbGUsIG1lZGlhbiwgbWVhbiwgM3JkIHF1YXJ0aWxlLCBhbmQgbWF4aW11bSB2YWx1ZXMsIG9mZmVyaW5nIGEgc3VjY2luY3Qgb3ZlcnZpZXcgb2YgZWFjaCB2YXJpYWJsZSdzIGRpc3RyaWJ1dGlvbiBhbmQgY2hhcmFjdGVyaXN0aWNzLiBUaGlzIHByb3ZpZGVzIGVzc2VudGlhbCBpbnNpZ2h0cyBmb3IgdGhlIGluaXRpYWwgZXhwbG9yYXRpb24gYW5kIHVuZGVyc3RhbmRpbmfCoG9mwqB0aGXCoGRhdGFzZXQuCldpdGggdXNpbmcgbWluaW11bSwgbWF4aW11bSwgbWVhbiwgbWVkaWFuIGxhd3MgaXQgaGVscHMgdG8gcHJvdmlkZSBhbiBvdmVydmlldyBvZiB0aGUgZGF0YSdzIGtleSBjaGFyYWN0ZXJpc3RpY3MKCmBgYHtyfQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5KQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRIYXJkbmVzcykKc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JFNvbGlkcykKc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JENobG9yYW1pbmVzKQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkUG90YWJpbGl0eSkKc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JFN1bGZhdGUpCnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRUcmloYWxvbWV0aGFuZXMpCnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRUdXJiaWRpdHkpCnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRwaCkKYGBgCgojIERhdGEgVHJhbnNmb3JtYXRpb246CgpUaGlzIHN0ZXAgaW52b2x2ZWQgdHJhbnNmb3JtaW5nIHRoZSBjbGFzcyBsYWJlbCwgUG90YWJpbGl0eSwgaW50byBjYXRlZ29yaWNhbCBkYXRhLiBXZSBjaGFuZ2VkIHRoZSBudW1lcmljIGRhdGEgdG8gJ05vdCBQb3RhYmxlJyBhbmQgJ1BvdGFibGUnIHRvIGluZGljYXRlIHdoZXRoZXIgdGhlIHdhdGVyIGlzIHNhZmUgZm9yIGh1bWFuIGNvbnN1bXB0aW9uLCB3aGVyZSAxIHJlcHJlc2VudHMgJ1BvdGFibGUnLCBhbmQgMCByZXByZXNlbnRzICdOb3QgUG90YWJsZS4KCmBgYHtyfQp3YXRlcl9wb3RhYmlsaXR5JFBvdGFiaWxpdHlbd2F0ZXJfcG90YWJpbGl0eSRQb3RhYmlsaXR5ID09ICcwJ10gPC0gJ05vdCBQb3RhYmxlJwp3YXRlcl9wb3RhYmlsaXR5JFBvdGFiaWxpdHlbd2F0ZXJfcG90YWJpbGl0eSRQb3RhYmlsaXR5ID09ICcxJ10gPC0gJ1BvdGFibGUnCgp3YXRlcl9wb3RhYmlsaXR5JFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHdhdGVyX3BvdGFiaWxpdHkkUG90YWJpbGl0eSkKdGFibGUod2F0ZXJfcG90YWJpbGl0eSRQb3RhYmlsaXR5KQoKYGBgCgpgYGB7cn0KcHJpbnQod2F0ZXJfcG90YWJpbGl0eSkKYGBgCgojIyMgb3V0bGllcnM6CgpUaGV5IGFyZSBvYnNlcnZhdGlvbnMgdGhhdCBsaWUgZmFyIGF3YXkgZnJvbSB0aGUgbWFqb3JpdHkgb2YgdGhlIGRhdGEuIE91dGxpZXJzIGNhbiBvY2N1ciBkdWUgdG8gdmFyaW91cyByZWFzb25zIHN1Y2ggYXMgbWVhc3VyZW1lbnQgZXJyb3JzLCBleHBlcmltZW50YWwgYW5vbWFsaWVzLCBvciBnZW51aW5lIGV4dHJlbWUgdmFsdWVzLgoKIyNiZWZvcmUgcmVtb3Zpbmcgb3V0bGllcjoKCmBgYHtyfQpkaW0od2F0ZXJfcG90YWJpbGl0eSkKaGVhZCh3YXRlcl9wb3RhYmlsaXR5KQpgYGAKCiMjIyMgcmVtb3Zpbmcgb3V0bGllcnM6CgpSZW1vdmluZyBvdXRsaWVycyBmcm9tIGEgZGF0YXNldCBpcyBjcml0aWNhbCBmb3IgYXNzdXJpbmcgdGhlIHF1YWxpdHkgYW5kIHJlbGlhYmlsaXR5IG9mIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGFuZCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gV2UgZm91bmQgYWxsIG91dGxpZXJzIGluIHRoZSBudW1lcmljYWwgYXR0cmlidXRlcyBhbmQgc3Vic2VxdWVudGx5IGVsaW1pbmF0ZWQgdGhlIHJvd3MgY29udGFpbmluZyB0aGUgb3V0bGllcnMuCgojIyMgLSBwaAoKYGBge3J9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRwaCkKcXVhcnRpbGVzIDwtIHF1YW50aWxlKHdhdGVyX3BvdGFiaWxpdHkkcGgsIHByb2JzID0gYyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpCnF1YXJ0aWxlcwppcXIgPC0gSVFSKHdhdGVyX3BvdGFiaWxpdHkkcGgpCmlxcgpsb3dlciA8LSBxdWFydGlsZXNbMV0gLSAxLjUqaXFyCmxvd2VyCnVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSppcXIKdXBwZXIKCmJveHBsb3QocGggfiBQb3RhYmlsaXR5LCBkYXRhID0gd2F0ZXJfcG90YWJpbGl0eSkKCnJlcGVhdCB7CiAgb3V0X3ZhbCA8LSBib3hwbG90KHdhdGVyX3BvdGFiaWxpdHkkcGgsIHlsYWIgPSAncGgnKSRvdXQKICBvdXRfdmFsCiAgb3V0X3Jvd3MgPC0gd2hpY2god2F0ZXJfcG90YWJpbGl0eSRwaCAlaW4lIGMob3V0X3ZhbCkpCiAgb3V0X3Jvd3MKICAKICBpZihzdW0ob3V0X3Jvd3MpID4gMCkgd2F0ZXJfcG90YWJpbGl0eSA8LSB3YXRlcl9wb3RhYmlsaXR5Wy1vdXRfcm93cyxdCiAgZWxzZSB7YnJlYWt9Cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JHBoKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBgCgojIyMgLUhhcmRuZXNzCgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JEhhcmRuZXNzKQpxdWFydGlsZXMgPC0gcXVhbnRpbGUod2F0ZXJfcG90YWJpbGl0eSRIYXJkbmVzcywgcHJvYnMgPSBjKC4yNSwgLjc1KSwgbmEucm0gPSBGQUxTRSkKcXVhcnRpbGVzCmlxciA8LSBJUVIod2F0ZXJfcG90YWJpbGl0eSRIYXJkbmVzcykKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChIYXJkbmVzcyB+IFBvdGFiaWxpdHksIGRhdGEgPSB3YXRlcl9wb3RhYmlsaXR5KQoKcmVwZWF0IHsKICBvdXRfdmFsIDwtIGJveHBsb3Qod2F0ZXJfcG90YWJpbGl0eSRIYXJkbmVzcywgeWxhYiA9ICdIYXJkbmVzcycpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JEhhcmRuZXNzICVpbiUgYyhvdXRfdmFsKSkKICBvdXRfcm93cwoKICBpZihzdW0ob3V0X3Jvd3MpID4gMCkgd2F0ZXJfcG90YWJpbGl0eSA8LSB3YXRlcl9wb3RhYmlsaXR5Wy1vdXRfcm93cyxdCiAgZWxzZSB7YnJlYWt9Cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JEhhcmRuZXNzKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBgCgojIyMgLVNvbGlkcwoKYGBge3J9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMpCnF1YXJ0aWxlcyA8LSBxdWFudGlsZSh3YXRlcl9wb3RhYmlsaXR5JFNvbGlkcywgcHJvYnMgPSBjKC4yNSwgLjc1KSwgbmEucm0gPSBGQUxTRSkKcXVhcnRpbGVzCmlxciA8LSBJUVIod2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMpCmlxcgpsb3dlciA8LSBxdWFydGlsZXNbMV0gLSAxLjUqaXFyCmxvd2VyCnVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSppcXIKdXBwZXIKCmJveHBsb3QoU29saWRzIH4gUG90YWJpbGl0eSwgZGF0YSA9IHdhdGVyX3BvdGFiaWxpdHkpCgpyZXBlYXQgewogIG91dF92YWwgPC0gYm94cGxvdCh3YXRlcl9wb3RhYmlsaXR5JFNvbGlkcywgeWxhYiA9ICdTb2xpZHMnKSRvdXQKICBvdXRfdmFsCiAgb3V0X3Jvd3MgPC0gd2hpY2god2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMgJWluJSBjKG91dF92YWwpKQogIG91dF9yb3dzCgogIGlmKHN1bShvdXRfcm93cykgPiAwKSB3YXRlcl9wb3RhYmlsaXR5IDwtIHdhdGVyX3BvdGFiaWxpdHlbLW91dF9yb3dzLF0KICBlbHNlIHticmVha30KfQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkU29saWRzKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBgCgojIyMgLUNobG9yYW1pbmVzCgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JENobG9yYW1pbmVzKQpxdWFydGlsZXMgPC0gcXVhbnRpbGUod2F0ZXJfcG90YWJpbGl0eSRDaGxvcmFtaW5lcywgcHJvYnMgPSBjKC4yNSwgLjc1KSwgbmEucm0gPSBGQUxTRSkKcXVhcnRpbGVzCmlxciA8LSBJUVIod2F0ZXJfcG90YWJpbGl0eSRDaGxvcmFtaW5lcykKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChDaGxvcmFtaW5lcyB+IFBvdGFiaWxpdHksIGRhdGEgPSB3YXRlcl9wb3RhYmlsaXR5KQoKcmVwZWF0IHsKICBvdXRfdmFsIDwtIGJveHBsb3Qod2F0ZXJfcG90YWJpbGl0eSRDaGxvcmFtaW5lcywgeWxhYiA9ICdDaGxvcmFtaW5lcycpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JENobG9yYW1pbmVzICVpbiUgYyhvdXRfdmFsKSkKICBvdXRfcm93cwoKICBpZihzdW0ob3V0X3Jvd3MpID4gMCkgd2F0ZXJfcG90YWJpbGl0eSA8LSB3YXRlcl9wb3RhYmlsaXR5Wy1vdXRfcm93cyxdCiAgZWxzZSB7YnJlYWt9Cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JENobG9yYW1pbmVzKQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmBgYAoKIyMjIC1TdWxmYXRlCgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JFN1bGZhdGUpCnF1YXJ0aWxlcyA8LSBxdWFudGlsZSh3YXRlcl9wb3RhYmlsaXR5JFN1bGZhdGUsIHByb2JzID0gYyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpCnF1YXJ0aWxlcwppcXIgPC0gSVFSKHdhdGVyX3BvdGFiaWxpdHkkU3VsZmF0ZSkKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChTdWxmYXRlIH4gUG90YWJpbGl0eSwgZGF0YSA9IHdhdGVyX3BvdGFiaWxpdHkpCgpyZXBlYXQgewogIG91dF92YWwgPC0gYm94cGxvdCh3YXRlcl9wb3RhYmlsaXR5JFN1bGZhdGUsIHlsYWIgPSAnU3VsZmF0ZScpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JFN1bGZhdGUgJWluJSBjKG91dF92YWwpKQogIG91dF9yb3dzCgogIGlmKHN1bShvdXRfcm93cykgPiAwKSB3YXRlcl9wb3RhYmlsaXR5IDwtIHdhdGVyX3BvdGFiaWxpdHlbLW91dF9yb3dzLF0KICBlbHNlIHticmVha30KfQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkU3VsZmF0ZSkKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpgYGAKCiMjIyAtQ29uZHVjdGl2aXR5CgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JENvbmR1Y3Rpdml0eSkKcXVhcnRpbGVzIDwtIHF1YW50aWxlKHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5LCBwcm9icyA9IGMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpxdWFydGlsZXMKaXFyIDwtIElRUih3YXRlcl9wb3RhYmlsaXR5JENvbmR1Y3Rpdml0eSkKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChDb25kdWN0aXZpdHkgfiBQb3RhYmlsaXR5LCBkYXRhID0gd2F0ZXJfcG90YWJpbGl0eSkKCnJlcGVhdCB7CiAgb3V0X3ZhbCA8LSBib3hwbG90KHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5LCB5bGFiID0gJ0NvbmR1Y3Rpdml0eScpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JENvbmR1Y3Rpdml0eSAlaW4lIGMob3V0X3ZhbCkpCiAgb3V0X3Jvd3MKCiAgaWYoc3VtKG91dF9yb3dzKSA+IDApIHdhdGVyX3BvdGFiaWxpdHkgPC0gd2F0ZXJfcG90YWJpbGl0eVstb3V0X3Jvd3MsXQogIGVsc2Uge2JyZWFrfQp9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRDb25kdWN0aXZpdHkpCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGAKCiMjIyAtT3JnYW5pY19jYXJib24KCmBgYHtyfQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCnF1YXJ0aWxlcyA8LSBxdWFudGlsZSh3YXRlcl9wb3RhYmlsaXR5JE9yZ2FuaWNfY2FyYm9uLCBwcm9icyA9IGMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpxdWFydGlsZXMKaXFyIDwtIElRUih3YXRlcl9wb3RhYmlsaXR5JE9yZ2FuaWNfY2FyYm9uKQppcXIKbG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41Kmlxcgpsb3dlcgp1cHBlciA8LSBxdWFydGlsZXNbMl0gKyAxLjUqaXFyCnVwcGVyCgpib3hwbG90KE9yZ2FuaWNfY2FyYm9uIH4gUG90YWJpbGl0eSwgZGF0YSA9IHdhdGVyX3BvdGFiaWxpdHkpCgpyZXBlYXQgewogIG91dF92YWwgPC0gYm94cGxvdCh3YXRlcl9wb3RhYmlsaXR5JE9yZ2FuaWNfY2FyYm9uLCB5bGFiID0gJ09yZ2FuaWNfY2FyYm9uJykkb3V0CiAgb3V0X3ZhbAogIG91dF9yb3dzIDwtIHdoaWNoKHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24gJWluJSBjKG91dF92YWwpKQogIG91dF9yb3dzCgogIGlmKHN1bShvdXRfcm93cykgPiAwKSB3YXRlcl9wb3RhYmlsaXR5IDwtIHdhdGVyX3BvdGFiaWxpdHlbLW91dF9yb3dzLF0KICBlbHNlIHticmVha30KfQpzdW1tYXJ5KHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGAKCiMjIyAtVHJpaGFsb21ldGhhbmVzCgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JFRyaWhhbG9tZXRoYW5lcykKcXVhcnRpbGVzIDwtIHF1YW50aWxlKHdhdGVyX3BvdGFiaWxpdHkkVHJpaGFsb21ldGhhbmVzLCBwcm9icyA9IGMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpxdWFydGlsZXMKaXFyIDwtIElRUih3YXRlcl9wb3RhYmlsaXR5JFRyaWhhbG9tZXRoYW5lcykKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChUcmloYWxvbWV0aGFuZXMgfiBQb3RhYmlsaXR5LCBkYXRhID0gd2F0ZXJfcG90YWJpbGl0eSkKCnJlcGVhdCB7CiAgb3V0X3ZhbCA8LSBib3hwbG90KHdhdGVyX3BvdGFiaWxpdHkkVHJpaGFsb21ldGhhbmVzLCB5bGFiID0gJ1RyaWhhbG9tZXRoYW5lcycpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JFRyaWhhbG9tZXRoYW5lcyAlaW4lIGMob3V0X3ZhbCkpCiAgb3V0X3Jvd3MKCiAgaWYoc3VtKG91dF9yb3dzKSA+IDApIHdhdGVyX3BvdGFiaWxpdHkgPC0gd2F0ZXJfcG90YWJpbGl0eVstb3V0X3Jvd3MsXQogIGVsc2Uge2JyZWFrfQp9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRUcmloYWxvbWV0aGFuZXMpCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGAKCiMjIyAtVHVyYmlkaXR5CgpgYGB7cn0Kc3VtbWFyeSh3YXRlcl9wb3RhYmlsaXR5JFR1cmJpZGl0eSkKcXVhcnRpbGVzIDwtIHF1YW50aWxlKHdhdGVyX3BvdGFiaWxpdHkkVHVyYmlkaXR5LCBwcm9icyA9IGMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpxdWFydGlsZXMKaXFyIDwtIElRUih3YXRlcl9wb3RhYmlsaXR5JFR1cmJpZGl0eSkKaXFyCmxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSppcXIKbG93ZXIKdXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41Kmlxcgp1cHBlcgoKYm94cGxvdChUdXJiaWRpdHkgfiBQb3RhYmlsaXR5LCBkYXRhID0gd2F0ZXJfcG90YWJpbGl0eSkKCnJlcGVhdCB7CiAgb3V0X3ZhbCA8LSBib3hwbG90KHdhdGVyX3BvdGFiaWxpdHkkVHVyYmlkaXR5LCB5bGFiID0gJ1R1cmJpZGl0eScpJG91dAogIG91dF92YWwKICBvdXRfcm93cyA8LSB3aGljaCh3YXRlcl9wb3RhYmlsaXR5JFR1cmJpZGl0eSAlaW4lIGMob3V0X3ZhbCkpCiAgb3V0X3Jvd3MKCiAgaWYoc3VtKG91dF9yb3dzKSA+IDApIHdhdGVyX3BvdGFiaWxpdHkgPC0gd2F0ZXJfcG90YWJpbGl0eVstb3V0X3Jvd3MsXQogIGVsc2Uge2JyZWFrfQp9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSRUdXJiaWRpdHkpCmBgYAoKIyMjIEFmdGVyIHJlbW92aW5nIG91dGxpZXJzOgoKYGBge3J9CmRpbSh3YXRlcl9wb3RhYmlsaXR5KQpzdHIod2F0ZXJfcG90YWJpbGl0eSkKaGVhZCh3YXRlcl9wb3RhYmlsaXR5KQpgYGAKCiMjIENoYXJ0czoKCnZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiBkYXRhIHRoYXQgaGVscCB1cyB1bmRlcnN0YW5kIGFuZCBhbmFseXplIGluZm9ybWF0aW9uIG1vcmUgZWFzaWx5LiBUaGV5IGNhbiBiZSB1c2VkIHRvIGRpc3BsYXkgdHJlbmRzLCBjb21wYXJpc29ucywgYW5kIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBkaWZmZXJlbnQgdmFyaWFibGVzLiBUaGVyZSBhcmUgdmFyaW91cyB0eXBlcyBvZiBjaGFydHMsIHN1Y2ggYXMKCiMjIyBIaXN0b2dyYW0KClRoZSBoaXN0b2dyYW0gc2hvd3MgdGhlIGZyZXF1ZW5jeSBvZiBwaCBpbiB0aGUgZGF0YXNldDsgd2Ugbm90ZWQgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdmFsdWVzIGZhbGwgd2l0aGluIHRoZSB1c3VhbCByYW5nZSwgd2hpY2ggaXMgYWJvdXQgYmV0d2VlbiA2IGFuZCA4LCBidXQgaXQgYWxzbyBzaG93cyBzZXZlcmFsIG91dGxpZXJzLgoKYGBge3J9Cmhpc3Qod2F0ZXJfcG90YWJpbGl0eSRwaCkKaGlzdCh3YXRlcl9wb3RhYmlsaXR5JENobG9yYW1pbmVzKQpoaXN0KHdhdGVyX3BvdGFiaWxpdHkkSGFyZG5lc3MpCmhpc3Qod2F0ZXJfcG90YWJpbGl0eSRTb2xpZHMpCmhpc3Qod2F0ZXJfcG90YWJpbGl0eSRTdWxmYXRlKQpoaXN0KHdhdGVyX3BvdGFiaWxpdHkkQ29uZHVjdGl2aXR5KQpoaXN0KHdhdGVyX3BvdGFiaWxpdHkkT3JnYW5pY19jYXJib24pCmhpc3Qod2F0ZXJfcG90YWJpbGl0eSRUcmloYWxvbWV0aGFuZXMpCmhpc3Qod2F0ZXJfcG90YWJpbGl0eSRUdXJiaWRpdHkpCmBgYAoKIyMjIEJhciBQbG90Cgp0aGUgYmFyIHBsb3QgcmVwcmVzZW50IGhvdyBwaCBsZXZlbHMgYWZmZWN0IHdhdGVyIHBvcnRhYmlsaXR5IGluIHRoZSBkYXRhc2V0IGl0IGluZGljYXRlcyB0aGF0IHBoIGxldmVsIGFib3ZlIDEwIGlzIG5vdCBwb3J0aWJhbCBhbmQgaHVtYW5zIGNhbnQgY29uc3VtZSBpdAoKYGBge3J9CnRhYiA8LSB3YXRlcl9wb3RhYmlsaXR5JFBvdGFiaWxpdHkgJT4lIHRhYmxlKCkKdHh0IDwtIHBhc3RlMCh0YWIpIApiYiA8LSB3YXRlcl9wb3RhYmlsaXR5JHBoICU+JSB0YWJsZSgpICU+JSBiYXJwbG90KCBtYWluPSdwaCcsY29sPWMoJ3BpbmsnKSkKYmIgPC0gd2F0ZXJfcG90YWJpbGl0eSRQb3RhYmlsaXR5ICU+JSB0YWJsZSgpICU+JSBiYXJwbG90KCBtYWluPSdQb3RhYmlsaXR5Jyx5bGFiPSdGcmVxdWVuY3knLGNvbD1jKCdwaW5rJywgJ2xpZ2h0Ymx1ZScpKQp0ZXh0KGJiLCB0YWIvMiwgbGFiZWxzPXR4dCwgY2V4PTEpCgpgYGAKCiMjIyBTY2F0dGVyIFBsb3QKClRoaXMgc2NhdHRlciBkZW1vbnN0cmF0ZXMgdGhlIGNvcnJlbGF0aW9uIGFuZCBwcm9wb3J0aW9uYWxpdHkgYmV0d2VlbiB0aGUgdHdvIHF1YWxpdGllcywgYWxsb3dpbmcgdXMgdG8gZXN0YWJsaXNoIHdoZXRoZXIgb3Igbm90IHR1cmJpZGl0eSBhbmQgcEggYXJlIGNvbm5lY3RlZC4KCmBgYHtyfQp3aXRoKHdhdGVyX3BvdGFiaWxpdHksIHBsb3QoVHJpaGFsb21ldGhhbmVzLCBwaCwgY29sID0gUG90YWJpbGl0eSwgcGNoID0gYXMubnVtZXJpYyhQb3RhYmlsaXR5KSkpCmBgYAoKIyMjIFJlbW92ZSBSZWR1bmRhbnQgRmVhdHVyZXM6CgpUaGlzIHdpbGwgZmluZCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZmVhdHVyZXMgYW5kIHJlcHJlc2VudCBpdCBpbiBoZWF0IG1hcAoKYGBge3J9CmNvcnJlbGF0aW9uX21hdHJpeCA8LSBjb3Iod2F0ZXJfcG90YWJpbGl0eVssMTo5XSkKaGlnaF9jb3JyZWxhdGlvbl9mZWF0dXJlcyA8LSBmaW5kQ29ycmVsYXRpb24oY29ycmVsYXRpb25fbWF0cml4LCBjdXRvZmYgPSAwLjUpCnByaW50KGhpZ2hfY29ycmVsYXRpb25fZmVhdHVyZXMpCmhlYXRtYXAoY29ycmVsYXRpb25fbWF0cml4KQpgYGAKCndlIHJlbW92ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZmVhdHVyZXMgYW5kIHJlcHJlc2VudCBpdCBpbiBoZWF0IG1hcAoKIyMjIEZlYXR1cmUgc2VsZWN0aW9uOgojIyMgRmVhdHVyZSBzZWxlY3Rpb24gaXMgZGVlbWVkIHVubmVjZXNzYXJ5IGZvciBvdXIgZGF0YXNldCwgYXMgZWFjaCBhdHRyaWJ1dGUgaXMgZGVlbWVkIHRvIHByb3ZpZGUgY3J1Y2lhbCwgZGlzdGluY3QgaW5mb3JtYXRpb24gcmVsYXRlZCB0byB0aGUgY2hvc2VuIHZhcmlhYmxlLiBUaGUgcmVhc29uaW5nIGJlaGluZCB0aGlzIGRlY2lzaW9uIGlzIHRoYXQgZWxpbWluYXRpbmcgYW55IGF0dHJpYnV0ZSB0aHJvdWdoIGZlYXR1cmUgc2VsZWN0aW9uIGNvdWxkIHBvdGVudGlhbGx5IGxlYWQgdG8gdGhlIGxvc3Mgb2Ygdml0YWwgZGF0YS4gQWRkaXRpb25hbGx5LCBjZXJ0YWluIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyBwb3NzZXNzIHRoZSBjYXBhYmlsaXR5IHRvIGlkZW50aWZ5IGludHJpY2F0ZSBpbnRlcmFjdGlvbnMgYW5kIGF0dHJpYnV0ZSBjb3JyZWxhdGlvbnMuIEJ5IGluY2x1ZGluZyBhbGwgdmFyaWFibGVzIGluIHRoZSBtb2RlbCwgd2UgYWltIHRvIGVuYWJsZSB0aGUgbGVhcm5pbmcgYW5kIGV4cGxvaXRhdGlvbiBvZiBjb21wbGV4IHBhdHRlcm5zIHdpdGhpbiB0aGUgZGF0YXNldCwgcG90ZW50aWFsbHkgZW5oYW5jaW5nIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UsIGVzcGVjaWFsbHkgY29uc2lkZXJpbmcgdGhlIGRhdGFzZXQncyBzdWJzdGFudGlhbCBzaXplIHdpdGjCoDEwwqBjb2x1bW5zCgppdCBpcyBjaG9vc2luZyB0aGUgbW9zdCByZWxldmFudCBmZWF0dXJlcyBmcm9tIGEgZGF0YXNldCB0byBlbmhhbmNlIG1vZGVsIHBlcmZvcm1hbmNlLCByZWR1Y2Ugb3ZlcmZpdHRpbmcsIGFuZCBpbXByb3ZlIGNvbXB1dGF0aW9uYWwgZWZmaWNpZW5jeS4KCgpSYW5raW5nIGZlYXR1cmVzIGJ5IGltcG9ydGFuY2UgaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBpZGVudGlmeSB0aGUgbW9zdCBpbmZsdWVudGlhbCB2YXJpYWJsZXMgaW4gYSBkYXRhc2V0IGZvciBwcmVkaWN0aW5nIGEgdGFyZ2V0IHZhcmlhYmxlLiBUaGlzIHByb2Nlc3MgaGVscHMgdW5kZXJzdGFuZCB3aGljaCBmZWF0dXJlcyBpbXBhY3QgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UgbW9zdCBieSByYW5raW5nIGZlYXR1cmVzIGJ5IGltcG9ydGFuY2UuCgpSZW1vdmluZyByZWR1bmRhbnQgZmVhdHVyZXMgbWVhbnMgZWxpbWluYXRpbmcgdmFyaWFibGVzIG9yIGZlYXR1cmVzIGZyb20gYSBkYXRhc2V0IHRoYXQgZG8gbm90IHByb3ZpZGUgYWRkaXRpb25hbCBvciB1bmlxdWXCoGluZm9ybWF0aW9uLgoKCgojIyMgUmFuayBGZWF0dXJlcyBCeSBJbXBvcnRhbmNlOgoKcmFua2luZyBmZWF0dXJlcyBieSBpbXBvcnRhbmNlIGlzIGEgdGVjaG5pcXVlIHVzZWQgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW5mbHVlbnRpYWwgdmFyaWFibGVzIGluIGEgZGF0YXNldCBmb3IgcHJlZGljdGluZyBhIHRhcmdldCB2YXJpYWJsZS4gVGhpcyBwcm9jZXNzIGhlbHBzIGluIHVuZGVyc3RhbmRpbmcgd2hpY2ggZmVhdHVyZXMgaGF2ZSB0aGUgbW9zdCBpbXBhY3Qgb24gdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UuIEJ5IHJhbmtpbmcgZmVhdHVyZXMgYnkgaW1wb3J0YW5jZS4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbCBhbmQgY2FsY3VsYXRlIGZlYXR1cmUgaW1wb3J0YW5jZQpyZiA9IHJhbmRvbUZvcmVzdCh4PSB3YXRlcl9wb3RhYmlsaXR5WywxOjldLHk9IHdhdGVyX3BvdGFiaWxpdHlbLDEwXSkKdmFyX2ltcCA8LSB2YXJJbXAocmYsIHNjYWxlID0gRkFMU0UpCiNzb3J0IHRoZSBzY29yZSBpbiBkZWNyZWFzaW5nIG9yZGVyCnZhcl9pbXBfZGYgPC0gZGF0YS5mcmFtZShjYmluZCh2YXJpYWJsZSA9IHJvd25hbWVzKHZhcl9pbXApLCBzY29yZSA9IHZhcl9pbXBbLDFdKSkKdmFyX2ltcF9kZiRzY29yZSA8LSBhcy5kb3VibGUodmFyX2ltcF9kZiRzY29yZSkKdmFyX2ltcF9kZltvcmRlcih2YXJfaW1wX2RmJHNjb3JlLGRlY3JlYXNpbmcgPSBUUlVFKSxdCgpnZ3Bsb3QodmFyX2ltcF9kZiwgYWVzKHg9cmVvcmRlcih2YXJpYWJsZSwgc2NvcmUpLCB5PXNjb3JlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc2VnbWVudChhZXMoeD12YXJpYWJsZSx4ZW5kPXZhcmlhYmxlLHk9MCx5ZW5kPXNjb3JlKSkgKwogIHlsYWIoIkluY05vZGVQdXJpdHkiKSArCiAgeGxhYigiVmFyaWFibGUgTmFtZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCgojIyMgUmVjdXJzaXZlIEZlYXR1cmUgZWxpbWluYXRpb246CgppcyBhIGZlYXR1cmUgc2VsZWN0aW9uIHRlY2huaXF1ZSB0aGF0IHJlY3Vyc2l2ZWx5IHJlbW92ZXMgbGVzcyBpbXBvcnRhbnQgZmVhdHVyZXMgZnJvbSBhIG1vZGVsIHVudGlsIHRoZSBvcHRpbWFsIHN1YnNldCBpcyBpZGVudGlmaWVkLiBJdCBpbnZvbHZlcyByZXBlYXRlZGx5IHRyYWluaW5nIHRoZSBtb2RlbCwgcmFua2luZyBmZWF0dXJlcyBiYXNlZCBvbiB0aGVpciBpbXBvcnRhbmNlLCBhbmQgZWxpbWluYXRpbmcgdGhlIGxlYXN0IGltcG9ydGFudCBvbmVzLgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiBjb250cm9sIDwtIHJmZUNvbnRyb2woZnVuY3Rpb25zPXJmRnVuY3MsIG1ldGhvZD0iY3YiLG51bWJlcj0xMCkKIHJmIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCwgdmVyYm9zZUl0ZXIgPSBGQUxTRSkKICMgcnVuIHRoZSBSRkUgYWxnb3JpdGhtCiByZmVfbW9kZWwgPC0gcmZlKHg9IHdhdGVyX3BvdGFiaWxpdHlbLDE6OV0seT0gd2F0ZXJfcG90YWJpbGl0eVssMTBdLCBzaXplcz1jKDE6OSksIHJmZUNvbnRyb2w9Y29udHJvbCkKICMgc3VtbWFyaXplIHRoZSByZXN1bHRzCiBwcmludChyZmVfbW9kZWwpCiAjIGxpc3QgdGhlIGNob3NlbiBmZWF0dXJlcwogcHJlZGljdG9ycyhyZmVfbW9kZWwpCiAjIHBsb3QgdGhlIHJlc3VsdHMKcGxvdChyZmVfbW9kZWwsIHR5cGU9YygiZyIsICJvIikpCmBgYCAKCiMjIyBEYXRhIHRyYW5zZm9ybWF0aW9uOgoKdGhlIHByb2Nlc3Mgb2YgY29udmVydGluZyBvciBtb2RpZnlpbmcgcmF3IGRhdGEgaW50byBhIGRpZmZlcmVudCBmb3JtYXQgb3Igc3RydWN0dXJlIHRvIG1ha2UgaXQgbW9yZSBzdWl0YWJsZSBmb3IgYW5hbHlzaXMgb3IgbW9kZWxpbmcKCiMjIyBOb3JtbGl6YXRpb24KCk5vcm1hbGl6YXRpb24gcmVmZXJzIHRvIHRoZSBwcm9jZXNzIG9mIHNjYWxpbmcgdmFyaWFibGVzIHRvIGhhdmUgYSBjb21tb24gcmFuZ2UuIEl0IGhlbHBzIGluIGNvbXBhcmluZyB2YXJpYWJsZXMgd2l0aCBkaWZmZXJlbnQgc2NhbGVzLgoKYGBge3J9CndwPC0gd2F0ZXJfcG90YWJpbGl0eQpub3JtYWxpemU9ZnVuY3Rpb24oeCl7cmV0dXJuICgoeC1taW4oeCkpLyhtYXgoeCkpKX0Kd3AkU29saWRzPW5vcm1hbGl6ZSh3cCRTb2xpZHMpCmBgYAoKVGhlIHNvbGlkcyBhdHRyaWJ1dGUgd2lsbCBjcmVhdGUgY3JpdGljYWwgY2hhbGxlbmdlcyBiZWNhdXNlIG9mIHRoZSB2YXN0IGFuZCBkaXZlcnRlZCB2YWx1ZXM6IG1pbiBpcyAzMjAuOSwgYW5kIG1heCBpcyA0MzE5NS41LCBzbyB3ZSBub3JtYWxpemVkIHRoZSBzb2xpZHMgYmV0d2VlbiAwIGFuZCAxIHRvIG1ha2UgdmFsdWVzIHNtYWxsZXIgYW5kIG1vcmUgcmVhc29uYWJsZS4KCiMjIyBEaXNjcmV0aXphdGlvbjoKCkRpc2NyZXRpemF0aW9uIGlzIHRoZSBwcm9jZXNzIG9mIHRyYW5zZm9ybWluZyBjb250aW51b3VzIHZhcmlhYmxlcyBpbnRvIGRpc2NyZXRlIG9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gSXQgY2FuIGJlIHVzZWZ1bCBmb3IgYW5hbHl6aW5nIGRhdGEgd2l0aCBtYW55IHVuaXF1ZSB2YWx1ZXMgb3Igc2ltcGxpZnlpbmcgaXQuCgpgYGB7cn0KCndwJHBoPSBjdXQod3AkcGgsIGJyZWFrcyA9IHNlcSgzLDExLGJ5PTQpLHJpZ2h0PUZBTFNFKQp3cCRIYXJkbmVzcz0gY3V0KHdwJEhhcmRuZXNzLCBicmVha3MgPSBzZXEoMTIwLDI4MCxieT00MCkscmlnaHQ9RkFMU0UpCndwJENobG9yYW1pbmVzID0gY3V0KHdwJENobG9yYW1pbmVzLCBicmVha3MgPSBzZXEoMywxMSxieT00KSxyaWdodCA9IEZBTFNFKQp3cCRTdWxmYXRlPSBjdXQod3AkU3VsZmF0ZSwgYnJlYWtzID0gc2VxKDIyMCw0NDAsYnk9NDQpLHJpZ2h0PUZBTFNFKQp3cCRDb25kdWN0aXZpdHk9IGN1dCh3cCRDb25kdWN0aXZpdHksIGJyZWFrcyA9IHNlcSgyMDAsNzAwLGJ5PTEwMCkscmlnaHQ9RkFMU0UpCndwJE9yZ2FuaWNfY2FyYm9uPSBjdXQod3AkT3JnYW5pY19jYXJib24sIGJyZWFrcyA9IHNlcSg0LDI0LGJ5PTQpLHJpZ2h0PUZBTFNFKQp3cCRUcmloYWxvbWV0aGFuZXM9IGN1dCh3cCRUcmloYWxvbWV0aGFuZXMsIGJyZWFrcyA9IHNlcSgyMCwxMTAsYnk9MTApLHJpZ2h0PUZBTFNFKQp3cCRUdXJiaWRpdHk9IGN1dCh3cCRUdXJiaWRpdHksIGJyZWFrcyA9IHNlcSgxLDcsYnk9MikscmlnaHQ9RkFMU0UpCgpwcmludCh3cCkKYGBgCgpUaGVyZWZvcmUsIHdlIHRyYW5zZm9ybWVkIHRoZSBjb250aW51b3VzIHZhbHVlcyBvZiB0aGUgbnVtZXJpYyBhdHRyaWJ1dGVzIGludG8gaW50ZXJ2YWxzIGJ5IGRpdmlkaW5nIHRoZSB2YWx1ZXMgdG8gZmFsbCBvbiBvbmUgb2YgdGhlIHBvc3NpYmxlIGludGVydmFsIGxhYmVscyBieSBkaXNjcmV0aXphdGlvbi4gVGhlIHZhbHVlcyB3aWxsIGJlIG1lYW5pbmdmdWwgYW5kIHNpbXBsZXIgdG8gY2xhc3NpZnkgb3IgcGVyZm9ybSBvdGhlciBtZXRob2RzIHRvIGhlbHAgdXMgbGF0ZXIgaW4gb3VyIG1vZGVsLiBTbywgSW4gVHJpaGFsb21ldGhhbmVzLCB3ZSBpbnRlcnZhbHMgYnkgZGl2aWRpbmcgdGhlIHZhbHVlcyBieSAxMCB0byBoYXZlIGxhYmVscyB3aXRoIGVxdWFsIHdpZHRoIDogWzIwLDMwKSBbMzAsNDApIFs0MCw1MCkgWzUwLDYwKSBbNjAsNzApIFs3MCw4MCkgWzgwLDkwKSBbOTAsMTAwKSBbMTAwLDExMCkuCgojIyMgIyNFbmNvZGluZwoKZW5jb2RpbmcgaXMgdGhlIHByb2Nlc3Mgb2YgY29udmVydGluZyBjaGFyYWN0ZXJzIG9yIHN0cmluZ3MgaW50byBhIHNwZWNpZmljIGVuY29kaW5nIGZvcm1hdC4gU2luY2Ugd2UgZG9uJ3QgaGF2ZSBhIE5vbWluYWwgYXR0cmlidXRlIGluIG91ciBkYXRhYmFzZSB3ZSBjb3VsZG4ndCBpbXBsZW1lbnQgaXQuCmBgYHtyfQp3cApgYGAKCiMjIGNsYXNzZmljYXRpb24gYW5kIGNsdXN0cmluZyBJbiBvdXIgZGF0YXNldCBleHBsb3JhdGlvbiwgd2UgZW1wbG95ZWQgYm90aCBzdXBlcnZpc2VkIGFuZCB1bnN1cGVydmlzZWQgbGVhcm5pbmcgbWV0aG9kb2xvZ2llcyB0aHJvdWdoIGNsYXNzaWZpY2F0aW9uIGFuZCBjbHVzdGVyaW5nIHRlY2huaXF1ZXMuCgogRm9yIGNsYXNzaWZpY2F0aW9uLCB3ZSBjaG9zZSB0aGUgZGVjaXNpb24gdHJlZSBhbGdvcml0aG0sIGEgcmVjdXJzaXZlIGFwcHJvYWNoIGNvbnN0cnVjdGluZyBhIHRyZWUgc3RydWN0dXJlIHdpdGggbGVhZiBub2RlcyBzaWduaWZ5aW5nIGZpbmFsIGRlY2lzaW9ucy4gVGhlIG9iamVjdGl2ZSB3YXMgdG8gcHJlZGljdCB0aGUgY2xhc3MgbGFiZWwgKHBvdGFiaWxpdHkpLCB3aXRoIHZhbHVlcyAwIG9yIDEsIGJhc2VkIG9uIGF0dHJpYnV0ZXMgbGlrZSBwSCwgSGFyZG5lc3MsIFNvbGlkcywgQ2hsb3JhbWluZXMsIFN1bGZhdGUsIENvbmR1Y3Rpdml0eSwgT3JnYW5pY19jYXJib24sIFRyaWhhbG9tZXRoYW5lcywgYW5kIFR1cmJpZGl0eS4gVGhlIGRhdGFzZXQgdW5kZXJ3ZW50IGRpdmlzaW9uIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyBmb3IgY29uc3RydWN0aW5nIGFuZCBldmFsdWF0aW5nIHRoZSBkZWNpc2lvbiB0cmVlLiBNb2RlbCBldmFsdWF0aW9uIGVuY29tcGFzc2VkIG1ldHJpY3MgbGlrZSBhY2N1cmFjeSBhbmQgY29zdC1zZW5zaXRpdmUgbWVhc3VyZXMsIGdhdWdlZCB1c2luZyBhIGNvbmZ1c2lvbiBtYXRyaXguIE91ciB0b29sa2l0IGluY2x1ZGVkIHBhY2thZ2VzIHN1Y2ggYXMgJ3BhcnR5JyBhbmQgJ2NhcmV0LCcgaW5jb3Jwb3JhdGluZyBtZXRob2RzIGxpa2UgJ3NhbXBsZScgZm9yIGRhdGEgc3BsaXR0aW5nLCAnY3RyZWUnIGZvciBkZWNpc2lvbiB0cmVlIGNvbnN0cnVjdGlvbiwgJ3ByZWRpY3QnIGZvciB0ZXN0aW5nIHByZWRpY3Rpb25zLCBhbmQgJ2NvbmZ1c2lvbk1hdHJpeCcgZm9yIG1vZGVsIGV2YWx1YXRpb24uCgogSW4gdGhlIHVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nIHBoYXNlLCB3ZSBleGNsdWRlZCB0aGUgY2xhc3MgbGFiZWwgYXR0cmlidXRlICJwb3RhYmlsaXR5IiBhbmQgdXRpbGl6ZWQgbnVtZXJpYyBhdHRyaWJ1dGVzIHN1Y2ggYXMgcEgsIEhhcmRuZXNzLCBTb2xpZHMsIENobG9yYW1pbmVzLCBTdWxmYXRlLCBDb25kdWN0aXZpdHksIE9yZ2FuaWNfY2FyYm9uLCBUcmloYWxvbWV0aGFuZXMsIGFuZCBUdXJiaWRpdHkuIEVtcGxveWluZyB0aGUgSy1tZWFucyBhbGdvcml0aG0sIGNsdXN0ZXJzIHdlcmUgZm9ybWVkLCBlYWNoIHJlcHJlc2VudGVkIGJ5IGEgY2VudGVyIHBvaW50LCBhbmQgb2JqZWN0cyB3ZXJlIGFzc2lnbmVkIHRvIHRoZSBuZWFyZXN0IGNsdXN0ZXIuIEZvciB0aGlzIHBoYXNlLCB3ZSBtYWRlIHVzZSBvZiBwYWNrYWdlcyBzdWNoIGFzICdjbHVzdGVyJyBhbmQgJ2ZhY3RvZXh0cmEsJyBpbmNvcnBvcmF0aW5nIG1ldGhvZHMgbGlrZSAnc2NhbGUoKScgZm9yIGRhdGEgc2NhbGluZywgJ0ttZWFucygpJyBmb3IgY2x1c3RlciBjcmVhdGlvbi4KCiBDbHVzdGVyIHZhbGlkYXRpb24gd2FzIHBlcmZvcm1lZCB1c2luZyB0aGUgJ3NpbGhvdWV0dGUoKScgbWV0aG9kIHRvIGNhbGN1bGF0ZSBhdmVyYWdlcyBmb3IgZWFjaCBjbHVzdGVyLiBJbiBib3RoIHN1cGVydmlzZWQgYW5kIHVuc3VwZXJ2aXNlZCB0ZWNobmlxdWVzLCB3ZSBtYWludGFpbmVkIHJlc3VsdCBjb25zaXN0ZW5jeSBieSBlbXBsb3lpbmcgdGhlICdzZXQuc2VlZCgpJyBtZXRob2Qgd2l0aCB0aGUgc2FtZSByYW5kb20gbnVtYmVyIHdoZW4gZXhwZXJpbWVudGluZyB3aXRoIGRpZmZlcmVudMKgZGF0YXNldMKgc2l6ZXMuCgoKI1RyYWluaW5nIHRlY2huaXF1ZQoKIEluIHRoZSBwcm92aWRlZCBjb2RlLCB3ZSBzeXN0ZW1hdGljYWxseSBhZGRyZXNzZWQgb3V0bGllcnMgaW4gbXVsdGlwbGUgY29sdW1ucyBvZiBvdXIgZGF0YXNldC4gQmVnaW5uaW5nIHdpdGggYSBzdW1tYXJ5IG9mIGVhY2ggY29sdW1uJ3Mgc3RhdGlzdGljcywgaW5jbHVkaW5nIHF1YXJ0aWxlcyBhbmQgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUiksIHdlIGVzdGFibGlzaGVkIG91dGxpZXIgZGV0ZWN0aW9uIGxpbWl0cy4gQSB2aXN1YWwgYXNzZXNzbWVudCB3YXMgY29uZHVjdGVkIHVzaW5nIGJveHBsb3RzLCBjYXRlZ29yaXplZCBieSByZWxldmFudCB2YXJpYWJsZXMgc3VjaCBhcyAiUG90YWJpbGl0eS4iIEEgbG9vcCB3YXMgaW1wbGVtZW50ZWQgdG8gaXRlcmF0aXZlbHkgaWRlbnRpZnkgYW5kIHJlbW92ZSBvdXRsaWVycyBpbiBlYWNoIGNvbHVtbiwgZW5zdXJpbmcgYSByb2J1c3QgY2xlYW5zaW5nIHByb2Nlc3MuIFRoZSBmaW5hbCBzdGVwIGludm9sdmVkIHN1bW1hcml6aW5nIHRoZSBjb2x1bW5zIHBvc3Qtb3V0bGllciByZW1vdmFsLCBvZmZlcmluZyBpbnNpZ2h0cyBpbnRvIHRoZSBpbXBhY3Qgb24gdGhlIGRpc3RyaWJ1dGlvbiBvZiBlYWNoIHZhcmlhYmxlLiBUaGlzIGNvbXByZWhlbnNpdmUgYXBwcm9hY2ggd2FzIGFwcGxpZWQgdW5pZm9ybWx5IHRvIGFsbCBkYXRhc2V0IGNvbHVtbnMsIHByb21vdGluZyBjb25zaXN0ZW5jeSBpbiB0aGUgb3V0bGllci1oYW5kbGluZ8KgcHJvY2Vzcy4KCgojIyMgSW5mb3JtYXRpb24gZ2FpbiAoSUQzKQpJbiB0aGlzIFIgY29kZSwgdGhlIGRhdGFzZXQgIHVuZGVyZ29lcyBhIHByb2Nlc3Mgb2YgdHJhaW5pbmcgYW5kIHRlc3RpbmcgdXNpbmcgdGhlIElEMyBhbGdvcml0aG0gZm9yIGRlY2lzaW9uIHRyZWUgY2xhc3NpZmljYXRpb24uIFRoZSBkYXRhIGlzIHN1Y2Nlc3NpdmVseSBzcGxpdCBpbnRvIHRyYWluaW5nIHNldHMgb2YgNzAlLCA4MCUsIGFuZCA5MCUsIHdpdGggY29ycmVzcG9uZGluZyB0ZXN0aW5nIHNldHMgb2YgMzAlLCAyMCUsIGFuZCAxMCUuIFRoZSBkZWNpc2lvbiB0cmVlIG1vZGVscyBhcmUgdHJhaW5lZCBvbiB0aGVzZSBzdWJzZXRzLCB1dGlsaXppbmcgZmVhdHVyZXMgc3VjaCBhcyBwSCwgaGFyZG5lc3MsIHNvbGlkcywgY2hsb3JhbWluZXMsIHN1bGZhdGUsIGNvbmR1Y3Rpdml0eSwgb3JnYW5pYyBjYXJib24sIHRyaWhhbG9tZXRoYW5lcywgYW5kIHR1cmJpZGl0eSB0byBwcmVkaWN0IHdhdGVyIHBvdGFiaWxpdHkuCgpGb3IgZWFjaCBzcGxpdCwgdGhlIGRlY2lzaW9uIHRyZWUgbW9kZWxzIGFyZSBldmFsdWF0ZWQgb24gdGhlaXIgcmVzcGVjdGl2ZSB0ZXN0aW5nIHNldHMsIGFuZCBwZXJmb3JtYW5jZSBtZXRyaWNzIHN1Y2ggYXMgY29uZnVzaW9uIG1hdHJpY2VzIGFuZCBhY2N1cmFjeSBhcmUgY29tcHV0ZWQuIEFkZGl0aW9uYWxseSwgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIGN1cnZlcyBhcmUgZ2VuZXJhdGVkLCBwcm92aWRpbmcgaW5zaWdodHMgaW50byB0aGUgbW9kZWxzJyBkaXNjcmltaW5hdGlvbiBjYXBhYmlsaXRpZXMuIFRoZSBBcmVhIFVuZGVyIHRoZSBDdXJ2ZSAoQVVDKSBpcyBjYWxjdWxhdGVkIGFzIGEgcXVhbnRpdGF0aXZlIG1lYXN1cmUgb2YgbW9kZWwgcGVyZm9ybWFuY2UuIFRoaXMgY29tcHJlaGVuc2l2ZSBhcHByb2FjaCBlbmFibGVzIGEgc3lzdGVtYXRpYyBleHBsb3JhdGlvbiBvZiB0aGUgSUQzIGRlY2lzaW9uIHRyZWUncyBlZmZlY3RpdmVuZXNzIGluIHByZWRpY3Rpbmcgd2F0ZXIgcG90YWJpbGl0eSB1bmRlciB2YXJ5aW5nIHRyYWluaW5nIGFuZCB0ZXN0aW5nwqBzY2VuYXJpb3MuCgoKU3BsaXR0aW5nIHRoZSBkYXRhIHNldCBpbnRvIHR3byBzdWJzZXRzOiBUcmFpbmluZyg3MCUpIGFuZCBUZXN0aW5nKDMwJSk6CmBgYHtyfQpzZXQuc2VlZCgxOTU4KQppbmQgPC0gc2FtcGxlKDIsIG5yb3cod3ApLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC43LCAwLjMpKQp0cmFpbi5kYXRhIDwtIHdwW2luZCA9PSAxLCBdCnRlc3QuZGF0YSA8LSB3cFtpbmQgPT0gMiwgXQp0cmFpbi5kYXRhJFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHRyYWluLmRhdGEkUG90YWJpbGl0eSkKdGVzdC5kYXRhJFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHRlc3QuZGF0YSRQb3RhYmlsaXR5KQoKCm15Rm9ybXVsYSA8LSBQb3RhYmlsaXR5IH4gcGgrSGFyZG5lc3MrU29saWRzK0NobG9yYW1pbmVzK1N1bGZhdGUrQ29uZHVjdGl2aXR5K09yZ2FuaWNfY2FyYm9uK1RyaWhhbG9tZXRoYW5lcytUdXJiaWRpdHkKI215Rm9ybXVsYSA8LSBQb3RhYmlsaXR5IH4gcGgrSGFyZG5lc3MrU29saWRzK0NobG9yYW1pbmVzK1N1bGZhdGUKCgptLmN0cmVlIDwtIGN0cmVlKG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluLmRhdGEpCnRhYmxlKHByZWRpY3QobS5jdHJlZSksIHRyYWluLmRhdGEkUG90YWJpbGl0eSkKCnByaW50KG0uY3RyZWUpCnBsb3QobS5jdHJlZSwgdHlwZT0ic2ltcGxlIikKCnRlc3RQcmVkIDwtIHByZWRpY3QobS5jdHJlZSwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKcmVzdWx0PC10YWJsZSh0ZXN0UHJlZCwgdGVzdC5kYXRhJFBvdGFiaWxpdHkpCgoKY29fcmVzdWx0IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHQpCnByaW50KGNvX3Jlc3VsdCkKYXMubWF0cml4KGNvX3Jlc3VsdCwgd2hhdCA9ICJjbGFzc2VzIikKYWNjIDwtIGNvX3Jlc3VsdCRvdmVyYWxsWyJBY2N1cmFjeSJdCmFjYyoxMDAKCgpwcmVkX3Byb2JzIDwtIGFzLm51bWVyaWMocHJlZGljdChtLmN0cmVlLCBuZXdkYXRhID0gdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikpCmJpbmFyeV9vdXRjb21lIDwtIGFzLm51bWVyaWModGVzdC5kYXRhJFBvdGFiaWxpdHkgPT0gIlBvdGFibGUiKQojIFJPQyBjdXJ2ZQpyb2NfY3VydmUgPC0gcm9jKGJpbmFyeV9vdXRjb21lLCBwcmVkX3Byb2JzKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCmBgYAoKU3BsaXR0aW5nIHRoZSBkYXRhIHNldCBpbnRvIHR3byBzdWJzZXRzOiBUcmFpbmluZyg4MCUpIGFuZCBUZXN0aW5nKDIwJSk6CmBgYHtyfQoKc2V0LnNlZWQoMTk1OCkKaW5kIDwtIHNhbXBsZSgyLCBucm93KHdwKSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKDAuOCwgMC4yKSkKdHJhaW4uZGF0YSA8LSB3cFtpbmQgPT0gMSwgXQp0ZXN0LmRhdGEgPC0gd3BbaW5kID09IDIsIF0KdHJhaW4uZGF0YSRQb3RhYmlsaXR5IDwtIGFzLmZhY3Rvcih0cmFpbi5kYXRhJFBvdGFiaWxpdHkpCgpteUZvcm11bGEgPC0gUG90YWJpbGl0eSB+IHBoK0hhcmRuZXNzK1NvbGlkcytDaGxvcmFtaW5lcytTdWxmYXRlK0NvbmR1Y3Rpdml0eStPcmdhbmljX2NhcmJvbitUcmloYWxvbWV0aGFuZXMrVHVyYmlkaXR5CiNteUZvcm11bGEgPC0gUG90YWJpbGl0eSB+IHBoK0hhcmRuZXNzK1NvbGlkcytDaGxvcmFtaW5lcytTdWxmYXRlCgoKbS5jdHJlZSA8LSBjdHJlZShteUZvcm11bGEsIGRhdGEgPSB0cmFpbi5kYXRhKQp0YWJsZShwcmVkaWN0KG0uY3RyZWUpLCB0cmFpbi5kYXRhJFBvdGFiaWxpdHkpCgpwcmludChtLmN0cmVlKQpwbG90KG0uY3RyZWUsIHR5cGU9InNpbXBsZSIpCgp0ZXN0UHJlZCA8LSBwcmVkaWN0KG0uY3RyZWUsIG5ld2RhdGEgPSB0ZXN0LmRhdGEpCnJlc3VsdDwtdGFibGUodGVzdFByZWQsIHRlc3QuZGF0YSRQb3RhYmlsaXR5KQoKCmNvX3Jlc3VsdCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0KQpwcmludChjb19yZXN1bHQpCmFzLm1hdHJpeChjb19yZXN1bHQsIHdoYXQgPSAiY2xhc3NlcyIpCmFjYyA8LSBjb19yZXN1bHQkb3ZlcmFsbFsiQWNjdXJhY3kiXQphY2MqMTAwCgpwcmVkX3Byb2JzIDwtIGFzLm51bWVyaWMocHJlZGljdChtLmN0cmVlLCBuZXdkYXRhID0gdGVzdC5kYXRhLCB0eXBlID0gInJlc3BvbnNlIikpCmJpbmFyeV9vdXRjb21lIDwtIGFzLm51bWVyaWModGVzdC5kYXRhJFBvdGFiaWxpdHkgPT0gIlBvdGFibGUiKQojIFJPQyBjdXJ2ZQpyb2NfY3VydmUgPC0gcm9jKGJpbmFyeV9vdXRjb21lLCBwcmVkX3Byb2JzKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCmBgYAoKU3BsaXR0aW5nIHRoZSBkYXRhIHNldCBpbnRvIHR3byBzdWJzZXRzOiBUcmFpbmluZyg5MCUpIGFuZCBUZXN0aW5nKDEwJSk6CmBgYHtyfQpzZXQuc2VlZCgxOTU4KQppbmQgPC0gc2FtcGxlKDIsIG5yb3cod3ApLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC45LCAwLjEpKQp0cmFpbi5kYXRhIDwtIHdwW2luZCA9PSAxLCBdCnRlc3QuZGF0YSA8LSB3cFtpbmQgPT0gMiwgXQp0cmFpbi5kYXRhJFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHRyYWluLmRhdGEkUG90YWJpbGl0eSkKCm15Rm9ybXVsYSA8LSBQb3RhYmlsaXR5IH4gcGgrSGFyZG5lc3MrU29saWRzK0NobG9yYW1pbmVzK1N1bGZhdGUrQ29uZHVjdGl2aXR5K09yZ2FuaWNfY2FyYm9uK1RyaWhhbG9tZXRoYW5lcytUdXJiaWRpdHkKI215Rm9ybXVsYSA8LSBQb3RhYmlsaXR5IH4gcGgrSGFyZG5lc3MrU29saWRzK0NobG9yYW1pbmVzK1N1bGZhdGUKCgptLmN0cmVlIDwtIGN0cmVlKG15Rm9ybXVsYSwgZGF0YSA9IHRyYWluLmRhdGEpCnRhYmxlKHByZWRpY3QobS5jdHJlZSksIHRyYWluLmRhdGEkUG90YWJpbGl0eSkKCnByaW50KG0uY3RyZWUpCnBsb3QobS5jdHJlZSwgdHlwZT0ic2ltcGxlIikKCnRlc3RQcmVkIDwtIHByZWRpY3QobS5jdHJlZSwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkKcmVzdWx0PC10YWJsZSh0ZXN0UHJlZCwgdGVzdC5kYXRhJFBvdGFiaWxpdHkpCgoKY29fcmVzdWx0IDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHQpCnByaW50KGNvX3Jlc3VsdCkKYXMubWF0cml4KGNvX3Jlc3VsdCwgd2hhdCA9ICJjbGFzc2VzIikKYWNjIDwtIGNvX3Jlc3VsdCRvdmVyYWxsWyJBY2N1cmFjeSJdCmFjYyoxMDAKCnByZWRfcHJvYnMgPC0gYXMubnVtZXJpYyhwcmVkaWN0KG0uY3RyZWUsIG5ld2RhdGEgPSB0ZXN0LmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKSkKYmluYXJ5X291dGNvbWUgPC0gYXMubnVtZXJpYyh0ZXN0LmRhdGEkUG90YWJpbGl0eSA9PSAiUG90YWJsZSIpCiMgUk9DIGN1cnZlCnJvY19jdXJ2ZSA8LSByb2MoYmluYXJ5X291dGNvbWUsIHByZWRfcHJvYnMpCnBsb3Qocm9jX2N1cnZlLCBtYWluID0gIlJPQyBDdXJ2ZSIsIGNvbCA9ICJibHVlIiwgbHdkID0gMikKYWJsaW5lKGEgPSAwLCBiID0gMSwgY29sID0gImdyYXkiLCBsdHkgPSAyKQojIFByaW50IEFVQwpjYXQoIkFVQzoiLCBhdWMocm9jX2N1cnZlKSwgIlxuIikKYGBgCgojIyMgR2FpbiByYXRpbyAoQzQuNSkKYSBkZWNpc2lvbiB0cmVlLWJhc2VkIGNsYXNzaWZpZXIsIGlzIHV0aWxpemVkIGZvciBtb2RlbCB0cmFpbmluZyBhbmQgZXZhbHVhdGlvbi4gVGhlIGRhdGFzZXQgdW5kZXJnb2VzIGNyb3NzLXZhbGlkYXRpb24gd2l0aCBkaWZmZXJlbnQgZm9sZCBzZXR0aW5ncyAoMyBmb2xkcywgNSBmb2xkcywgYW5kIDEwIGZvbGRzKS4gRm9yIGVhY2ggZm9sZCBjb25maWd1cmF0aW9uLCBhIEo0OCBtb2RlbCBpcyB0cmFpbmVkLCBhbmQgaXRzIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UgaXMgYXNzZXNzZWQgdXNpbmcgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIGN1cnZlcy4gVGhlIEFyZWEgVW5kZXIgdGhlIEN1cnZlIGlzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggUk9DIGN1cnZlLCBwcm92aWRpbmcgYSBxdWFudGl0YXRpdmUgbWVhc3VyZSBvZiB0aGUgbW9kZWwncyBhYmlsaXR5IHRvIHByZWRpY3Qgd2F0ZXIgcG90YWJpbGl0eS4gTm90YWJseSwgdGhlIHZpc3VhbCBpbnNwZWN0aW9uIG9mIHRoZSBST0MgY3VydmVzIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCB0cmFpbmVkIHdpdGggMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGV4aGliaXRzIHRoZSBoaWdoZXN0IGRpc2NyaW1pbmF0aXZlIHBlcmZvcm1hbmNlLiBUaGlzIGNvbXBhcmF0aXZlIGFuYWx5c2lzIGFjcm9zcyB2YXJpb3VzIGNyb3NzLXZhbGlkYXRpb24gc2NlbmFyaW9zIG9mZmVycyB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSByb2J1c3RuZXNzIGFuZCBnZW5lcmFsaXphdGlvbsKgY2FwYWJpbGl0eS4KCgpgYGB7cn0KIyAzIGZvbGRzCnNldC5zZWVkKDE5NTgpCnRyYWluIDwtIGNyZWF0ZUZvbGRzKHdwJFBvdGFiaWxpdHksIGs9MykKQzQ1Rml0IDwtIHRyYWluKFBvdGFiaWxpdHkgfiAuLG1ldGhvZCA9ICJKNDgiLGRhdGEgPSB3cCwKICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbCgKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjdiIsCiAgICAgICAgICAgICAgICBpbmRleCA9IHRyYWluLAogICAgICAgICAgICAgICAgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkpCgpDNDVGaXQKCkM0NUZpdCRmaW5hbE1vZGVsCgpwcmVkX3Byb2JzIDwtIHByZWRpY3QoQzQ1Rml0LCBuZXdkYXRhID0gd3AsIHR5cGUgPSAicHJvYiIpWywgIlBvdGFibGUiXQpiaW5hcnlfb3V0Y29tZSA8LSBhcy5udW1lcmljKHdwJFBvdGFiaWxpdHkgPT0gIlBvdGFibGUiKQojIFJPQyBjdXJ2ZQpyb2NfY3VydmUgPC0gcm9jKGJpbmFyeV9vdXRjb21lLCBwcmVkX3Byb2JzKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCmBgYAoKYGBge3J9CiMgNSBmb2xkcwpzZXQuc2VlZCgxOTU4KQp0cmFpbiA8LSBjcmVhdGVGb2xkcyh3cCRQb3RhYmlsaXR5LCBrPTUpCkM0NUZpdCA8LSB0cmFpbihQb3RhYmlsaXR5IH4uLCBtZXRob2Q9Iko0OCIsIGRhdGE9d3AsCiAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2woCiAgICAgICAgICAgICAgICBtZXRob2QgPSJjdiIsIAogICAgICAgICAgICAgICAgaW5kZXggPSB0cmFpbiwKICAgICAgICAgICAgICAgIHNhdmVQcmVkaWN0aW9ucyA9IFRSVUUpKQoKQzQ1Rml0CgpDNDVGaXQkZmluYWxNb2RlbAoKcHJlZF9wcm9icyA8LSBwcmVkaWN0KEM0NUZpdCwgbmV3ZGF0YSA9IHdwLCB0eXBlID0gInByb2IiKVssICJQb3RhYmxlIl0KYmluYXJ5X291dGNvbWUgPC0gYXMubnVtZXJpYyh3cCRQb3RhYmlsaXR5ID09ICJQb3RhYmxlIikKIyBST0MgY3VydmUKcm9jX2N1cnZlIDwtIHJvYyhiaW5hcnlfb3V0Y29tZSwgcHJlZF9wcm9icykKcGxvdChyb2NfY3VydmUsIG1haW4gPSAiUk9DIEN1cnZlIiwgY29sID0gImJsdWUiLCBsd2QgPSAyKQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAiZ3JheSIsIGx0eSA9IDIpCiMgUHJpbnQgQVVDCmNhdCgiQVVDOiIsIGF1Yyhyb2NfY3VydmUpLCAiXG4iKQoKYGBgCgpgYGB7cn0KIyAxMCBmb2xkcwpzZXQuc2VlZCgxOTU4KQp0cmFpbiA8LSBjcmVhdGVGb2xkcyh3cCRQb3RhYmlsaXR5LCBrPTEwKQpDNDVGaXQgPC0gdHJhaW4oUG90YWJpbGl0eSB+LiwgbWV0aG9kPSJKNDgiLCBkYXRhPXdwLAogICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKAogICAgICAgICAgICAgICAgICBtZXRob2Q9ImN2IiwgaW5kZXhPdXQ9dHJhaW4pKQoKQzQ1Rml0CgpDNDVGaXQkZmluYWxNb2RlbAoKcHJlZF9wcm9icyA8LSBwcmVkaWN0KEM0NUZpdCwgbmV3ZGF0YSA9IHdwLCB0eXBlID0gInByb2IiKVssICJQb3RhYmxlIl0KYmluYXJ5X291dGNvbWUgPC0gYXMubnVtZXJpYyh3cCRQb3RhYmlsaXR5ID09ICJQb3RhYmxlIikKIyBST0MgY3VydmUKcm9jX2N1cnZlIDwtIHJvYyhiaW5hcnlfb3V0Y29tZSwgcHJlZF9wcm9icykKcGxvdChyb2NfY3VydmUsIG1haW4gPSAiUk9DIEN1cnZlIiwgY29sID0gImJsdWUiLCBsd2QgPSAyKQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAiZ3JheSIsIGx0eSA9IDIpCiMgUHJpbnQgQVVDCmNhdCgiQVVDOiIsIGF1Yyhyb2NfY3VydmUpLCAiXG4iKQoKYGBgCgpDNS4wIG5ld2VyIHZlcnNpb24gb2YgQzQuNQpTcGxpdHRpbmcgdGhlIGRhdGEgc2V0IGludG8gdHdvIHN1YnNldHM6IFRyYWluaW5nKDcwJSkgYW5kIFRlc3RpbmcoMzAlKToKYGBge3J9CnNldC5zZWVkKDE5NTgpCnRyYWluLmluZGljZXMgPC0gc2FtcGxlKDIsIG5yb3cod2F0ZXJfcG90YWJpbGl0eSksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkKdy50cmFpbiA8LSB3YXRlcl9wb3RhYmlsaXR5W3RyYWluLmluZGljZXMgPT0gMSwgXQp3LnRlc3QgPC0gd2F0ZXJfcG90YWJpbGl0eVt0cmFpbi5pbmRpY2VzID09IDIsIF0Kdy50cmFpbiRQb3RhYmlsaXR5IDwtIGFzLmZhY3Rvcih3LnRyYWluJFBvdGFiaWxpdHkpCgptb2RlbCA8LSBDNS4wKFBvdGFiaWxpdHkgfi4sIGRhdGE9dy50cmFpbikKCnJlc3VsdHMgPC0gcHJlZGljdChvYmplY3Q9bW9kZWwsIG5ld2RhdGE9dy50ZXN0LCB0eXBlPSJjbGFzcyIpCgp0YWJsZShyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKCnBsb3QobW9kZWwpCgpyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKYWNjIDwtIHIkb3ZlcmFsbFsiQWNjdXJhY3kiXSoxMDAKYWNjCmFzLm1hdHJpeChyLCB3aGF0ID0gImNsYXNzZXMiKQpwcmludChyKQoKCgpwcmVkX3Byb2JzIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB3LnRlc3QsIHR5cGUgPSAicHJvYiIpWywgIlBvdGFibGUiXQpiaW5hcnlfb3V0Y29tZSA8LSBhcy5udW1lcmljKHcudGVzdCRQb3RhYmlsaXR5ID09ICJQb3RhYmxlIikKIyBST0MgY3VydmUKcm9jX2N1cnZlIDwtIHJvYyhiaW5hcnlfb3V0Y29tZSwgcHJlZF9wcm9icykKcGxvdChyb2NfY3VydmUsIG1haW4gPSAiUk9DIEN1cnZlIiwgY29sID0gImJsdWUiLCBsd2QgPSAyKQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAiZ3JheSIsIGx0eSA9IDIpCiMgUHJpbnQgQVVDCmNhdCgiQVVDOiIsIGF1Yyhyb2NfY3VydmUpLCAiXG4iKQpgYGAKClNwbGl0dGluZyB0aGUgZGF0YSBzZXQgaW50byB0d28gc3Vic2V0czogVHJhaW5pbmcoODAlKSBhbmQgVGVzdGluZygyMCUpOgpgYGB7cn0Kc2V0LnNlZWQoMTk1OCkKdHJhaW4uaW5kaWNlcyA8LSBzYW1wbGUoMiwgbnJvdyh3YXRlcl9wb3RhYmlsaXR5KSwgcmVwbGFjZT1UUlVFLCBwcm9iPWMoMC44LCAwLjIpKQp3LnRyYWluIDwtIHdhdGVyX3BvdGFiaWxpdHlbdHJhaW4uaW5kaWNlcyA9PSAxLCBdCncudGVzdCA8LSB3YXRlcl9wb3RhYmlsaXR5W3RyYWluLmluZGljZXMgPT0gMiwgXQp3LnRyYWluJFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHcudHJhaW4kUG90YWJpbGl0eSkKCgptb2RlbCA8LSBDNS4wKFBvdGFiaWxpdHkgfi4sIGRhdGE9dy50cmFpbikKCnJlc3VsdHMgPC0gcHJlZGljdChvYmplY3Q9bW9kZWwsIG5ld2RhdGE9dy50ZXN0LCB0eXBlPSJjbGFzcyIpCgp0YWJsZShyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKCnBsb3QobW9kZWwpCgpyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKYWNjIDwtIHIkb3ZlcmFsbFsiQWNjdXJhY3kiXSoxMDAKYWNjCmFzLm1hdHJpeChyLCB3aGF0ID0gImNsYXNzZXMiKQpwcmludChyKQoKCnByZWRfcHJvYnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHcudGVzdCwgdHlwZSA9ICJwcm9iIilbLCAiUG90YWJsZSJdCmJpbmFyeV9vdXRjb21lIDwtIGFzLm51bWVyaWMody50ZXN0JFBvdGFiaWxpdHkgPT0gIlBvdGFibGUiKQojIFJPQyBjdXJ2ZQpyb2NfY3VydmUgPC0gcm9jKGJpbmFyeV9vdXRjb21lLCBwcmVkX3Byb2JzKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCmBgYAoKU3BsaXR0aW5nIHRoZSBkYXRhIHNldCBpbnRvIHR3byBzdWJzZXRzOiBUcmFpbmluZyg5MCUpIGFuZCBUZXN0aW5nKDEwJSk6CmBgYHtyfQpzZXQuc2VlZCgxOTU4KQp0cmFpbi5pbmRpY2VzIDwtIHNhbXBsZSgyLCBucm93KHdhdGVyX3BvdGFiaWxpdHkpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjksIDAuMSkpCncudHJhaW4gPC0gd2F0ZXJfcG90YWJpbGl0eVt0cmFpbi5pbmRpY2VzID09IDEsIF0Kdy50ZXN0IDwtIHdhdGVyX3BvdGFiaWxpdHlbdHJhaW4uaW5kaWNlcyA9PSAyLCBdCncudHJhaW4kUG90YWJpbGl0eSA8LSBhcy5mYWN0b3Iody50cmFpbiRQb3RhYmlsaXR5KQoKCm1vZGVsIDwtIEM1LjAoUG90YWJpbGl0eSB+LiwgZGF0YT13LnRyYWluKQoKcmVzdWx0cyA8LSBwcmVkaWN0KG9iamVjdD1tb2RlbCwgbmV3ZGF0YT13LnRlc3QsIHR5cGU9ImNsYXNzIikKCnRhYmxlKHJlc3VsdHMsIHcudGVzdCRQb3RhYmlsaXR5KQoKcGxvdChtb2RlbCkKYGBgCgoKIyMjVG8gaW1wcm92ZSB0aGUgcmVhZGFiaWxpdHkgb2YgdGhlIGRlY2lzaW9uIHRyZWUsIHdlIGRlY2lkZWQgdG8gc2FtcGxlIHRoZSBkYXRhIHVzaW5nIG9ubHkgdGhlIHBIIGFuZCBzdWxmYXRlIGF0dHJpYnV0ZXMuIFdlIHRoZW4gc3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIHVzaW5nIHRoZSBzYW1lIHNwbGl0IHBvaW50czoKIyMjVHJhaW5pbmcoOTAlKSBhbmQgVGVzdGluZygxMCUpLCB3aGljaCBhbGxvd2VkIGZvciBhIG1vcmUgbWFuYWdlYWJsZSBkZWNpc2lvbiB0cmVlOgpgYGB7cn0Kc2V0LnNlZWQoMTk1OCkKaW1wb3J0ZW50X2ZlYXR1cmVfc2FtcGxlIDwtIHNlbGVjdCh3YXRlcl9wb3RhYmlsaXR5LGMoMSw1LDEwKSkKdHJhaW4uaW5kaWNlcyA8LSBzYW1wbGUoMiwgbnJvdyhpbXBvcnRlbnRfZmVhdHVyZV9zYW1wbGUpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjksIDAuMSkpCncudHJhaW4gPC0gaW1wb3J0ZW50X2ZlYXR1cmVfc2FtcGxlW3RyYWluLmluZGljZXMgPT0gMSwgXQp3LnRlc3QgPC0gaW1wb3J0ZW50X2ZlYXR1cmVfc2FtcGxlW3RyYWluLmluZGljZXMgPT0gMiwgXQp3LnRyYWluJFBvdGFiaWxpdHkgPC0gYXMuZmFjdG9yKHcudHJhaW4kUG90YWJpbGl0eSkKCgptb2RlbCA8LSBDNS4wKFBvdGFiaWxpdHkgfi4sIGRhdGE9dy50cmFpbikKCnJlc3VsdHMgPC0gcHJlZGljdChvYmplY3Q9bW9kZWwsIG5ld2RhdGE9dy50ZXN0LCB0eXBlPSJjbGFzcyIpCgp0YWJsZShyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKCnBsb3QobW9kZWwpCgpyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRzLCB3LnRlc3QkUG90YWJpbGl0eSkKYWNjIDwtIHIkb3ZlcmFsbFsiQWNjdXJhY3kiXSoxMDAKYWNjCmFzLm1hdHJpeChyLCB3aGF0ID0gImNsYXNzZXMiKQpwcmludChyKQoKCgpwcmVkX3Byb2JzIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB3LnRlc3QsIHR5cGUgPSAicHJvYiIpWywgIlBvdGFibGUiXQpiaW5hcnlfb3V0Y29tZSA8LSBhcy5udW1lcmljKHcudGVzdCRQb3RhYmlsaXR5ID09ICJQb3RhYmxlIikKIyBST0MgY3VydmUKcm9jX2N1cnZlIDwtIHJvYyhiaW5hcnlfb3V0Y29tZSwgcHJlZF9wcm9icykKcGxvdChyb2NfY3VydmUsIG1haW4gPSAiUk9DIEN1cnZlIiwgY29sID0gImJsdWUiLCBsd2QgPSAyKQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAiZ3JheSIsIGx0eSA9IDIpCiMgUHJpbnQgQVVDCmNhdCgiQVVDOiIsIGF1Yyhyb2NfY3VydmUpLCAiXG4iKQpgYGAKCgoKCiMjI0dpbmkgaW5kZXggKENBUlQpCndlIGVtcGxveWVkIHRoZSBDNS4wIGFsZ29yaXRobSwgYW4gZW5oYW5jZWQgdmVyc2lvbiBvZiB0aGUgQzQuNSBkZWNpc2lvbiB0cmVlLCBmb3IgbW9kZWwgdHJhaW5pbmcgYW5kIGV2YWx1YXRpb24gYWNyb3NzIGRpZmZlcmVudCB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXQgc3BsaXRzLiBUaGUgZGF0YXNldCB1bmRlcndlbnQgdGhyZWUgc2NlbmFyaW9zOiBUcmFpbmluZyg3MCUpIGFuZCBUZXN0aW5nKDMwJSksIFRyYWluaW5nKDgwJSkgYW5kIFRlc3RpbmcoMjAlKSwgYW5kIFRyYWluaW5nKDkwJSkgYW5kIFRlc3RpbmcoMTAlKS4gRm9yIGVhY2ggY2FzZSwgdGhlIEM1LjAgbW9kZWwgd2FzIHRyYWluZWQgb24gdGhlIGRlc2lnbmF0ZWQgdHJhaW5pbmcgZGF0YSwgZXZhbHVhdGVkIG9uIHRoZSB0ZXN0aW5nIGRhdGEsIGFuZCBpdHMgcGVyZm9ybWFuY2Ugd2FzIGFzc2Vzc2VkIHRocm91Z2ggYWNjdXJhY3ksIGNvbmZ1c2lvbiBtYXRyaXgsIGFuZCBST0MgY3VydmUgd2l0aCBBcmVhIFVuZGVyIHRoZSBDdXJ2ZSAoQVVDKS4KClVwb24gY29tcGFyYXRpdmUgYW5hbHlzaXMsIGl0IHdhcyBvYnNlcnZlZCB0aGF0IHRoZSBtb2RlbCB0cmFpbmVkIHdpdGggYSBsYXJnZXIgcHJvcG9ydGlvbiBvZiBkYXRhIChUcmFpbmluZyA5MCUsIFRlc3RpbmcgMTAlKSBkZW1vbnN0cmF0ZWQgc3VwZXJpb3IgcGVyZm9ybWFuY2UsIGFjaGlldmluZyBoaWdoZXIgYWNjdXJhY3kgYW5kIGEgbW9yZSBkaXNjcmltaW5hdGl2ZSBST0MgY3VydmUuIFRoaXMgZXhwbG9yYXRpb24gYWNyb3NzIGRpZmZlcmVudCB0cmFpbmluZyBhbmQgdGVzdGluZyBzcGxpdHMgcHJvdmlkZXMgdmFsdWFibGUgaW5zaWdodHMgaW50byB0aGUgcm9idXN0bmVzcyBhbmQgZ2VuZXJhbGl6YXRpb24gY2FwYWJpbGl0eSBvZiB0aGUgQzUuMCBkZWNpc2lvbiB0cmVlIGFsZ29yaXRobSBmb3IgcHJlZGljdGluZyB3YXRlcsKgcG90YWJpbGl0eS4KClNwbGl0dGluZyB0aGUgZGF0YSBzZXQgaW50byB0d28gc3Vic2V0czogVHJhaW5pbmcoNzAlKSBhbmQgVGVzdGluZygzMCUpOgpgYGB7cn0Kc2V0LnNlZWQoMTk1OCkKdHJhaW4gPSBzYW1wbGUoMiwgbnJvdyh3cCksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNywgMC4zKSkKd3AudHJhaW49d3BbdHJhaW4gPT0gMSxdCndwLnRlc3Q9d3BbdHJhaW4gPT0gMixdCgoKCmZpdC50cmVlID0gcnBhcnQoUG90YWJpbGl0eSB+IC4sIGRhdGE9d3AsIG1ldGhvZCA9ICJjbGFzcyIsIGNwPTAuMDA4KQpmaXQudHJlZQoKcnBhcnQucGxvdChmaXQudHJlZSkKCmZpdC50cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UKCnByZWQudHJlZSA9IHByZWRpY3QoZml0LnRyZWUsIHdwLnRlc3QsIHR5cGUgPSAiY2xhc3MiKQpyZSA8LSB0YWJsZShwcmVkLnRyZWUsIHdwLnRlc3QkUG90YWJpbGl0eSkKCmNvX3JlIDwtIGNvbmZ1c2lvbk1hdHJpeChyZSkKcHJpbnQoY29fcmUpCmFzLm1hdHJpeChjb19yZSwgd2hhdCA9ICJjbGFzc2VzIikKYWNjIDwtIGNvX3JlJG92ZXJhbGxbIkFjY3VyYWN5Il0KYWNjKjEwMAoKcGxvdGNwKGZpdC50cmVlKQpwcmludGNwKGZpdC50cmVlKQoKIyBFeHBsaWNpdGx5IHJlcXVlc3QgdGhlIGxvd2VzdCBjcCB2YWx1ZQpmaXQudHJlZSRjcHRhYmxlW3doaWNoLm1pbihmaXQudHJlZSRjcHRhYmxlWywieGVycm9yIl0pLCJDUCJdCgpiZXN0Y3AgPC1maXQudHJlZSRjcHRhYmxlW3doaWNoLm1pbihmaXQudHJlZSRjcHRhYmxlWywieGVycm9yIl0pLCJDUCJdCnBydW5lZC50cmVlIDwtIHBydW5lKGZpdC50cmVlLCBjcCA9IGJlc3RjcCkKcnBhcnQucGxvdChwcnVuZWQudHJlZSkKCnByZWQucHJ1bmUgPSBwcmVkaWN0KHBydW5lZC50cmVlLCB3cC50ZXN0LCB0eXBlPSJjbGFzcyIpCgpyZSA8LSB0YWJsZShwcmVkLnBydW5lLCB3cC50ZXN0JFBvdGFiaWxpdHkpCgpjb19yZSA8LSBjb25mdXNpb25NYXRyaXgocmUpCnByaW50KGNvX3JlKQphcy5tYXRyaXgoY29fcmUsIHdoYXQgPSAiY2xhc3NlcyIpCmFjYyA8LSBjb19yZSRvdmVyYWxsWyJBY2N1cmFjeSJdCmFjYyoxMDAKCgoKcHJlZC50cmVlX3JhdyA8LSBwcmVkaWN0KGZpdC50cmVlLCB3cC50ZXN0KQojIENvbnZlcnQgdG8gcHJvYmFiaWxpdGllcwpwcmVkLnRyZWVfcHJvYnMgPC0gZXhwKHByZWQudHJlZV9yYXcpIC8gKDEgKyBleHAocHJlZC50cmVlX3JhdykpCiMgRXh0cmFjdCBwcm9iYWJpbGl0aWVzIGZvciB0aGUgIlBvdGFibGUiIGNsYXNzCnJvY19jdXJ2ZSA8LSByb2MoaWZlbHNlKHdwLnRlc3QkUG90YWJpbGl0eSA9PSAiUG90YWJsZSIsIDEsIDApLCBwcmVkLnRyZWVfcHJvYnNbLCAiUG90YWJsZSJdKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCgoKYGBgCgpTcGxpdHRpbmcgdGhlIGRhdGEgc2V0IGludG8gdHdvIHN1YnNldHM6IFRyYWluaW5nKDgwJSkgYW5kIFRlc3RpbmcoMjAlKToKYGBge3J9CnNldC5zZWVkKDE5NTgpCnRyYWluID0gc2FtcGxlKDIsIG5yb3cod3ApLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjgsIDAuMikpCndwLnRyYWluPXdwW3RyYWluID09IDEsXQp3cC50ZXN0PXdwW3RyYWluID09IDIsXQoKCgpmaXQudHJlZSA9IHJwYXJ0KFBvdGFiaWxpdHkgfiAuLCBkYXRhPXdwLnRyYWluLCBtZXRob2QgPSAiY2xhc3MiLCBjcD0wLjAwOCkKZml0LnRyZWUKCnJwYXJ0LnBsb3QoZml0LnRyZWUpCgpmaXQudHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlCgpwcmVkLnRyZWUgPSBwcmVkaWN0KGZpdC50cmVlLCB3cC50ZXN0LCB0eXBlID0gImNsYXNzIikKcmUgPC0gdGFibGUocHJlZC50cmVlLCB3cC50ZXN0JFBvdGFiaWxpdHkpCgpjb19yZSA8LSBjb25mdXNpb25NYXRyaXgocmUpCnByaW50KGNvX3JlKQphcy5tYXRyaXgoY29fcmUsIHdoYXQgPSAiY2xhc3NlcyIpCmFjYyA8LSBjb19yZSRvdmVyYWxsWyJBY2N1cmFjeSJdCmFjYyoxMDAKCnBsb3RjcChmaXQudHJlZSkKcHJpbnRjcChmaXQudHJlZSkKCiMgRXhwbGljaXRseSByZXF1ZXN0IHRoZSBsb3dlc3QgY3AgdmFsdWUKZml0LnRyZWUkY3B0YWJsZVt3aGljaC5taW4oZml0LnRyZWUkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQoKYmVzdGNwIDwtZml0LnRyZWUkY3B0YWJsZVt3aGljaC5taW4oZml0LnRyZWUkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQpwcnVuZWQudHJlZSA8LSBwcnVuZShmaXQudHJlZSwgY3AgPSBiZXN0Y3ApCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUpCgpwcmVkLnBydW5lID0gcHJlZGljdChwcnVuZWQudHJlZSwgd3AudGVzdCwgdHlwZT0iY2xhc3MiKQoKcmUgPC0gdGFibGUocHJlZC5wcnVuZSwgd3AudGVzdCRQb3RhYmlsaXR5KQoKY29fcmUgPC0gY29uZnVzaW9uTWF0cml4KHJlKQpwcmludChjb19yZSkKYXMubWF0cml4KGNvX3JlLCB3aGF0ID0gImNsYXNzZXMiKQphY2MgPC0gY29fcmUkb3ZlcmFsbFsiQWNjdXJhY3kiXQphY2MqMTAwCgoKcHJlZC50cmVlX3JhdyA8LSBwcmVkaWN0KGZpdC50cmVlLCB3cC50ZXN0KQpwcmVkLnRyZWVfcHJvYnMgPC0gZXhwKHByZWQudHJlZV9yYXcpIC8gKDEgKyBleHAocHJlZC50cmVlX3JhdykpCnJvY19jdXJ2ZSA8LSByb2MoaWZlbHNlKHdwLnRlc3QkUG90YWJpbGl0eSA9PSAiUG90YWJsZSIsIDEsIDApLCBwcmVkLnRyZWVfcHJvYnNbLCAiUG90YWJsZSJdKQpwbG90KHJvY19jdXJ2ZSwgbWFpbiA9ICJST0MgQ3VydmUiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmFibGluZShhID0gMCwgYiA9IDEsIGNvbCA9ICJncmF5IiwgbHR5ID0gMikKIyBQcmludCBBVUMKY2F0KCJBVUM6IiwgYXVjKHJvY19jdXJ2ZSksICJcbiIpCgoKYGBgCgpTcGxpdHRpbmcgdGhlIGRhdGEgc2V0IGludG8gdHdvIHN1YnNldHM6IFRyYWluaW5nKDkwJSkgYW5kIFRlc3RpbmcoMTAlKToKYGBge3J9CnNldC5zZWVkKDE5NTgpCnRyYWluID0gc2FtcGxlKDIsIG5yb3cod3ApLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjksIDAuMSkpCndwLnRyYWluPXdwW3RyYWluID09IDEsXQp3cC50ZXN0PXdwW3RyYWluID09IDIsXQoKCgpmaXQudHJlZSA9IHJwYXJ0KFBvdGFiaWxpdHkgfiAuLCBkYXRhPXdwLnRyYWluLCBtZXRob2QgPSAiY2xhc3MiLCBjcD0wLjAwOCkKZml0LnRyZWUKCnJwYXJ0LnBsb3QoZml0LnRyZWUpCgpmaXQudHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlCgpwcmVkLnRyZWUgPSBwcmVkaWN0KGZpdC50cmVlLCB3cC50ZXN0LCB0eXBlID0gImNsYXNzIikKcmUgPC0gdGFibGUocHJlZC50cmVlLCB3cC50ZXN0JFBvdGFiaWxpdHkpCgpjb19yZSA8LSBjb25mdXNpb25NYXRyaXgocmUpCnByaW50KGNvX3JlKQphcy5tYXRyaXgoY29fcmUsIHdoYXQgPSAiY2xhc3NlcyIpCmFjYyA8LSBjb19yZSRvdmVyYWxsWyJBY2N1cmFjeSJdCmFjYyoxMDAKCnBsb3RjcChmaXQudHJlZSkKcHJpbnRjcChmaXQudHJlZSkKCiMgRXhwbGljaXRseSByZXF1ZXN0IHRoZSBsb3dlc3QgY3AgdmFsdWUKZml0LnRyZWUkY3B0YWJsZVt3aGljaC5taW4oZml0LnRyZWUkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQoKYmVzdGNwIDwtZml0LnRyZWUkY3B0YWJsZVt3aGljaC5taW4oZml0LnRyZWUkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQpwcnVuZWQudHJlZSA8LSBwcnVuZShmaXQudHJlZSwgY3AgPSBiZXN0Y3ApCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUpCgpwcmVkLnBydW5lID0gcHJlZGljdChwcnVuZWQudHJlZSwgd3AudGVzdCwgdHlwZT0iY2xhc3MiKQoKcmUgPC0gdGFibGUocHJlZC5wcnVuZSwgd3AudGVzdCRQb3RhYmlsaXR5KQoKY29fcmUgPC0gY29uZnVzaW9uTWF0cml4KHJlKQpwcmludChjb19yZSkKYXMubWF0cml4KGNvX3JlLCB3aGF0ID0gImNsYXNzZXMiKQphY2MgPC0gY29fcmUkb3ZlcmFsbFsiQWNjdXJhY3kiXQphY2MqMTAwCgoKCnByZWQudHJlZV9yYXcgPC0gcHJlZGljdChmaXQudHJlZSwgd3AudGVzdCkKcHJlZC50cmVlX3Byb2JzIDwtIGV4cChwcmVkLnRyZWVfcmF3KSAvICgxICsgZXhwKHByZWQudHJlZV9yYXcpKQpyb2NfY3VydmUgPC0gcm9jKGlmZWxzZSh3cC50ZXN0JFBvdGFiaWxpdHkgPT0gIlBvdGFibGUiLCAxLCAwKSwgcHJlZC50cmVlX3Byb2JzWywgIlBvdGFibGUiXSkKcGxvdChyb2NfY3VydmUsIG1haW4gPSAiUk9DIEN1cnZlIiwgY29sID0gImJsdWUiLCBsd2QgPSAyKQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAiZ3JheSIsIGx0eSA9IDIpCiMgUHJpbnQgQVVDCmNhdCgiQVVDOiIsIGF1Yyhyb2NfY3VydmUpLCAiXG4iKQpgYGAKIyMjIENvbXBhcmlzb24gQ3JpdGVyaWE6Cgp8ICAgICAgICAgICAgfEluZm9ybWF0aW9uIEdhaW46ICAgICAgICAgICAgICAgfEdhaW4gUmF0aW86ICAgICAgICAgICAgICAgICAgICAgfCBHaW5pIGluZGV4OiAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBBY2N1cmFjeSAgIHwgMC41ODIwNjExICAgICAgICAgICAgICAgICAgICAgIHwgMC45Njg2NzgxICAgICAgICAgICAgICAgICAgICAgIHwgMC41OTM1MTE1ICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHByZWNpc2lvbiAgfCAwLjU4NTQ2MTY5ICAgICAgICAgICAgICAgICAgICAgfCAwLjAwNTQxNzEzMiAgICAgICAgICAgICAgICAgICAgfCAwLjYwMDQzMjAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgCnwgc2Vuc2l0aXZpdHl8IDAuOTczODU2MjEgICAgICAgICAgICAgICAgICAgICB8IDAuOTMzNDc0MSAgICAgICAgICAgICAgICAgICAgICB8IDAuOTA4NDk2NyAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgCnwgc3BlY2lmaWNpdHl8IDAuMDMyMTEwMDkgICAgICAgICAgICAgICAgICAgICB8IDAuODgwNjc3ICAgICAgICAgICAgICAgICAgICAgICB8IDAuMTUxMzc2MSAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApBcyBzaG93biBpbiB0aGUgY29tcGFyaXNvbiB0aGUgYmVzdCB0ZWNobmlxdWUgdG8gY2hvb3NlIGlzIEdhaW4gUmFpbyBEdWUgdG8gdGhlIGhpZ2ggYWNjdXJhY3kgCgoKYGBge3J9CnN1bW1hcnkod2F0ZXJfcG90YWJpbGl0eSkKc3RyKHdhdGVyX3BvdGFiaWxpdHkpCmBgYAoKIyMgQ2x1c3RlcmluZwoKIyBkZXRlcm1pbmUgYW5kIHZpc3VhbGl6ZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVyczoKCndlIHdpbGwgdXNlIGZvdXIgZGlmZmVyZW50IHNpemVzIG9mIGsgZm9yIGNsdXN0ZXJpbmcgYW5kIHRoZW4gc2VlIHdoYXQgcGVyZm9ybXMgYmVzdCBiZXR3ZWVuIHRoZW0uCgojIyMgU2NhbGUgZGF0YSBmaXJzdDoKCkNvbmZpcm0gdGhhdCBhbGwgdGhlIGNvbHVtbnMgeW91IGFyZSB0cnlpbmcgdG8gc2NhbGUgYXJlIGluZGVlZCBudW1lcmljLiBZb3UgY2FuIHVzZSBzYXBwbHkoKSB0byBjaGVjayBhbmQgY29lcmNlIHRoZW0gdG8gbnVtZXJpYyBpZiBuZWNlc3NhcnkuCgpzaW5lYyBhbGwgY291bG1lIGFyZSBudW1lcmljIHdlIHdsbCBzY2FsZSBhbGwgb2YgdGhlbSBleHBldCBjbGFzcyBsYWJlbCBhbmQgd2Ugc2F2ZWQgaXQgaW4gZGF0YXNldCBjYWxsZWQgQ2x1c3RlciBhbmQgd2UgdXNlZCBpdCBpbiBDbHVzdHJpbmcKCmBgYHtyfQp3YXRlcl9wb3RhYmlsaXR5PC0gc2FwcGx5KHdhdGVyX3BvdGFiaWxpdHksIGFzLm51bWVyaWMpCgpkYXRhX2Zvcl9jbHVzdGVyIDwtIHNjYWxlKHdhdGVyX3BvdGFiaWxpdHlbLCAhY29sbmFtZXMod2F0ZXJfcG90YWJpbGl0eSkgJWluJSAiUG90YWJpbGl0eSJdKQojd2UgdXNlICFjb2xuYW1lcyh3YXRlcl9wb3RhYmlsaXR5KSAlaW4lICJQb3RhYmlsaXR5IiB0byBleGNsdWRlIHRoZSAiUG90YWJpbGl0eSIgY29sdW1uClZpZXcoZGF0YV9mb3JfY2x1c3RlcikKYGBgCgojIyBDbHVzdHJpbmcxCgojIyMgSy1tZWFucwoKYGBge3J9CiMgMy0gcnVuIGstbWVhbnMgY2x1c3RlcmluZyB0byBmaW5kIDIgY2x1c3RlcnMKI3NldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiAgdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUKc2V0LnNlZWQoODk1MykKa21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YV9mb3JfY2x1c3RlciwyKQojIHByaW50IHRoZSBjbHVzdGVybmcgcmVzdWx0CmttZWFucy5yZXN1bHQKYGBgCgojIyMgdmlzdWFsaXplIGNsdXN0ZXJpbmcKCmBgYHtyfQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nICgyIGNsdXN0ZXJzKQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IGRhdGFfZm9yX2NsdXN0ZXIpCmBgYAoKIyMjIGhpZXJjcmNoaWNhbCBjbHVzdGVyaW5nCgp3ZSB0b29rIDUwIHNhbXBsZSB0byBtYWtlIGl0IG1vcmUgdW5kZXJzdGFiYWxlCgpgYGB7cn0KIyBkcmF3IGEgc2FtcGxlIG9mIDUwIHJlY29yZHMgZnJvbSB0aGUgZGF0YSwgc28gdGhhdCB0aGUgY2x1c3RlcmluZyBwbG90IHdpbGwgbm90IGJlIG92ZXIgY3Jvd2RlZCBhbmQgZWFzeSB0byB1bmRyZXN0YW5kIAppZHg8LXNhbXBsZSgxOmRpbShkYXRhX2Zvcl9jbHVzdGVyKVsxXSwgNTApCnNhbXBsZV9jMTwtZGF0YV9mb3JfY2x1c3RlcltpZHgsIF0KCiMjIGhpZXJjcmNoaWNhbCBjbHVzdGVyaW5nCmhjLmN1dDwtIGhjdXQoc2FtcGxlX2MxLCBrID0gMiwgaGNfbWV0aG9kPSAiY29tcGxldGUiKQpgYGAKCiMjIyBWaXN1YWxpemUgZGVuZHJvZ3JhbSBhbmQgc2FtcGxlIENsdXN0ZXJpbmcKCmRlbmRyb2dyYW0gaXMgYSB0cmVlIGRpYWdyYW0gdGhhdCBkaXNwbGF5cyB0aGUgYXJyYW5nZW1lbnQgb2YgZGF0YSBwb2ludHMgaW4gYSBoaWVyYXJjaGljYWwgb3JkZXIgYmFzZWQgb24gdGhlaXIgc2ltaWxhcml0eSBvciBkaXNzaW1pbGFyaXR5LgoKYGBge3J9CiMgVmlzdWFsaXplIGRlbmRyb2dyYW0KZnZpel9kZW5kKGhjLmN1dCxyZWN0PSBUUlVFKQojIFZpc3VhbGl6ZSBjbHVzdGVyCmZ2aXpfY2x1c3RlcihoYy5jdXQsIGVsbGlwc2UudHlwZT0gImNvbnZleCIpCmBgYAoKIyMjIGF2ZXJhZ2Ugc2lsaG91ZXR0ZQoKVGhpcyBtZXRob2QgY2FsY3VsYXRlcyB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGssIGRldGVybWluaW5nIGhvdyB3ZWxsIGRhdGEgcG9pbnRzIGZpdCBpbnRvIHRoZWlyIGFzc2lnbmVkIGNsdXN0ZXJzLgoKYGBge3J9CiAjYXZlcmFnZSBzaWxob3VldHRlIGZvciBlYWNoIGNsdXN0ZXJzIAoKYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFfZm9yX2NsdXN0ZXIpKSAjYSBkaXNzaW1pbGFyaXR5IG9iamVjdCBpbmhlcml0aW5nIGZyb20gY2xhc3MgZGlzdCBvciBjb2VyY2libGUgdG8gb25lLiBJZiBub3Qgc3BlY2lmaWVkLCBkbWF0cml4IG11c3QgYmUuCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKSNrLW1lYW5zIGNsdXN0ZXJpbmcgd2l0aCBlc3RpbWF0aW5nIGsgYW5kIGluaXRpYWxpemF0aW9ucwpgYGAKCiMjIyBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKCkJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbCBhcmUgbWV0cmljcyB1c2VkIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBjbHVzdGVyaW5nIGFsZ29yaXRobXMsIHBhcnRpY3VsYXJseSBpbiB0aGUgY29udGV4dCBvZiBldmFsdWF0aW5nIHRoZSBxdWFsaXR5IG9mIGNsdXN0ZXJpbmcgYXNzaWdubWVudHMgZm9yIGluZGl2aWR1YWwgZGF0YSBwb2ludHMuCgpgYGB7cn0KCmNsdXN0ZXJfYXNzaWdubWVudHMgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpCmdyb3VuZF90cnV0aF9sYWJlbHMgPC0gYyh3YXRlcl9wb3RhYmlsaXR5KQpkYXRhIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykKCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbApjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YSkgewogIG4gPC0gbnJvdyhkYXRhKQogIHByZWNpc2lvbl9zdW0gPC0gMAogIHJlY2FsbF9zdW0gPC0gMAoKICBmb3IgKGkgaW4gMTpuKSB7CiAgICBjbHVzdGVyIDwtIGRhdGEkY2x1c3RlcltpXQogICAgbGFiZWwgPC0gZGF0YSRsYWJlbFtpXQogICAgCiMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBmcm9tIHRoZSBzYW1lIGNhdGVnb3J5IHdpdGhpbiB0aGUgc2FtZSBjbHVzdGVyCnNhbWVfY2F0ZWdvcnlfc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhJGxhYmVsW2RhdGEkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkKICAgIAojIENvdW50IHRoZSB0b3RhbCBudW1iZXIgb2YgaXRlbXMgaW4gdGhlIHNhbWUgY2x1c3Rlcgp0b3RhbF9zYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGEkY2x1c3RlciA9PSBjbHVzdGVyKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyB3aXRoIHRoZSBzYW1lIGNhdGVnb3J5CnRvdGFsX3NhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGEkbGFiZWwgPT0gbGFiZWwpCiAgICAKIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwgZm9yIHRoZSBjdXJyZW50IGl0ZW0gYW5kIGFkZCB0aGVtIHRvIHRoZSBzdW1zCnByZWNpc2lvbl9zdW0gPC0gcHJlY2lzaW9uX3N1bSArIHNhbWVfY2F0ZWdvcnlfc2FtZV9jbHVzdGVyIC90b3RhbF9zYW1lX2NsdXN0ZXIKcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgLyB0b3RhbF9zYW1lX2NhdGVnb3J5CiAgfQoKICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuCiAgcmVjYWxsIDwtIHJlY2FsbF9zdW0gLyBuCgogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkKfQoKIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGEpCgojIEV4dHJhY3QgcHJlY2lzaW9uIGFuZCByZWNhbGwgZnJvbSB0aGUgbWV0cmljcwpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsCgojIFByaW50IHRoZSByZXN1bHRzCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpCmNhdCgiQkN1YmVkIFJlY2FsbDoiLCByZWNhbGwsICJcbiIpCgpgYGAKCiMjIENsdXN0cmluZzIKCiMjIyBLLW1lYW5zCgpydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgMyBjbHVzdGVycyBzZXQgYSBzZWVkIGZvciByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUKCmBgYHtyfQpzZXQuc2VlZCg4OTUzKQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhkYXRhX2Zvcl9jbHVzdGVyLDMpCiMgcHJpbnQgdGhlIENsdXN0cmluZyByZXN1bHQKa21lYW5zLnJlc3VsdApgYGAKCiMjIyB2aXN1YWxpemUgY2x1c3RlcmluZwoKYGBge3J9CiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcgKDMgY2x1c3RlcnMpCgpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IGRhdGFfZm9yX2NsdXN0ZXIpCmBgYAoKIyMjIGhpZXJjcmNoaWNhbCBjbHVzdGVyaW5nCgpgYGB7cn0KIyBkcmF3IGEgc2FtcGxlIG9mIDUwIHJlY29yZHMgZnJvbSB0aGUgZGF0YSwgc28gdGhhdCB0aGUgY2x1c3RlcmluZyBwbG90IHdpbGwgbm90IGJlIG92ZXIgY3Jvd2RlZCBhbmQgZWFzeSB0byB1bmRyZXN0YW5kIAoKaWR4Mjwtc2FtcGxlKDE6ZGltKGRhdGFfZm9yX2NsdXN0ZXIpWzFdLCA1MCkKc2FtcGxlX2MyPC1kYXRhX2Zvcl9jbHVzdGVyW2lkeDIsIF0KCiMjIGhpZXJjcmNoaWNhbCBjbHVzdGVyaW5nCmhjMi5jdXQ8LSBoY3V0KHNhbXBsZV9jMiwgayA9IDMsIGhjX21ldGhvZD0gImNvbXBsZXRlIikKYGBgCgojIyMgVmlzdWFsaXplIGRlbmRyb2dyYW0KCmRlbmRyb2dyYW0gaXMgYSB0cmVlIGRpYWdyYW0gdGhhdCBkaXNwbGF5cyB0aGUgYXJyYW5nZW1lbnQgb2YgZGF0YSBwb2ludHMgaW4gYSBoaWVyYXJjaGljYWwgb3JkZXIgYmFzZWQgb24gdGhlaXIgc2ltaWxhcml0eSBvciBkaXNzaW1pbGFyaXR5LgoKYGBge3J9CmZ2aXpfZGVuZChoYzIuY3V0LHJlY3Q9IFRSVUUpCiMgVmlzdWFsaXplIGNsdXN0ZXIKZnZpel9jbHVzdGVyKGhjMi5jdXQsIGVsbGlwc2UudHlwZT0gImNvbnZleCIpCmBgYAoKIyMjIGF2ZXJhZ2Ugc2lsaG91ZXR0ZQoKVGhpcyBtZXRob2QgY2FsY3VsYXRlcyB0aGUgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGssIGRldGVybWluaW5nIGhvdyB3ZWxsIGRhdGEgcG9pbnRzIGZpdCBpbnRvIHRoZWlyIGFzc2lnbmVkIGNsdXN0ZXJzLgoKYGBge3J9CiAjYXZlcmFnZSBzaWxob3VldHRlIGZvciBlYWNoIGNsdXN0ZXJzIAoKYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlcixkaXN0KGRhdGFfZm9yX2NsdXN0ZXIpKSAjYSBkaXNzaW1pbGFyaXR5IG9iamVjdCBpbmhlcml0aW5nIGZyb20gY2xhc3MgZGlzdCBvciBjb2VyY2libGUgdG8gb25lLiBJZiBub3Qgc3BlY2lmaWVkLCBkbWF0cml4IG11c3QgYmUuCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKSNrLW1lYW5zIGNsdXN0ZXJpbmcgd2l0aCBlc3RpbWF0aW5nIGsgYW5kIGluaXRpYWxpemF0aW9ucwpgYGAKCiMjIyBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKCkJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbCBhcmUgbWV0cmljcyB1c2VkIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBjbHVzdGVyaW5nIGFsZ29yaXRobXMsIHBhcnRpY3VsYXJseSBpbiB0aGUgY29udGV4dCBvZiBldmFsdWF0aW5nIHRoZSBxdWFsaXR5IG9mIGNsdXN0ZXJpbmcgYXNzaWdubWVudHMgZm9yIGluZGl2aWR1YWwgZGF0YSBwb2ludHMuCgpgYGB7cn0KCmNsdXN0ZXJfYXNzaWdubWVudHMgPC0gYyhrbWVhbnMucmVzdWx0JGNsdXN0ZXIpCmdyb3VuZF90cnV0aF9sYWJlbHMgPC0gYyh3YXRlcl9wb3RhYmlsaXR5KQpkYXRhIDwtIGRhdGEuZnJhbWUoY2x1c3RlciA9IGNsdXN0ZXJfYXNzaWdubWVudHMsIGxhYmVsID0gZ3JvdW5kX3RydXRoX2xhYmVscykKCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbApjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MgPC0gZnVuY3Rpb24oZGF0YSkgewogIG4gPC0gbnJvdyhkYXRhKQogIHByZWNpc2lvbl9zdW0gPC0gMAogIHJlY2FsbF9zdW0gPC0gMAoKICBmb3IgKGkgaW4gMTpuKSB7CiAgICBjbHVzdGVyIDwtIGRhdGEkY2x1c3RlcltpXQogICAgbGFiZWwgPC0gZGF0YSRsYWJlbFtpXQogICAgCiMgQ291bnQgdGhlIG51bWJlciBvZiBpdGVtcyBmcm9tIHRoZSBzYW1lIGNhdGVnb3J5IHdpdGhpbiB0aGUgc2FtZSBjbHVzdGVyCnNhbWVfY2F0ZWdvcnlfc2FtZV9jbHVzdGVyIDwtIHN1bShkYXRhJGxhYmVsW2RhdGEkY2x1c3RlciA9PSBjbHVzdGVyXSA9PSBsYWJlbCkKICAgIAojIENvdW50IHRoZSB0b3RhbCBudW1iZXIgb2YgaXRlbXMgaW4gdGhlIHNhbWUgY2x1c3Rlcgp0b3RhbF9zYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGEkY2x1c3RlciA9PSBjbHVzdGVyKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyB3aXRoIHRoZSBzYW1lIGNhdGVnb3J5CnRvdGFsX3NhbWVfY2F0ZWdvcnkgPC0gc3VtKGRhdGEkbGFiZWwgPT0gbGFiZWwpCiAgICAKIyBDYWxjdWxhdGUgcHJlY2lzaW9uIGFuZCByZWNhbGwgZm9yIHRoZSBjdXJyZW50IGl0ZW0gYW5kIGFkZCB0aGVtIHRvIHRoZSBzdW1zCnByZWNpc2lvbl9zdW0gPC0gcHJlY2lzaW9uX3N1bSArIHNhbWVfY2F0ZWdvcnlfc2FtZV9jbHVzdGVyIC90b3RhbF9zYW1lX2NsdXN0ZXIKcmVjYWxsX3N1bSA8LSByZWNhbGxfc3VtICsgc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgLyB0b3RhbF9zYW1lX2NhdGVnb3J5CiAgfQoKICAjIENhbGN1bGF0ZSBhdmVyYWdlIHByZWNpc2lvbiBhbmQgcmVjYWxsCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbl9zdW0gLyBuCiAgcmVjYWxsIDwtIHJlY2FsbF9zdW0gLyBuCgogIHJldHVybihsaXN0KHByZWNpc2lvbiA9IHByZWNpc2lvbiwgcmVjYWxsID0gcmVjYWxsKSkKfQoKIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCm1ldHJpY3MgPC0gY2FsY3VsYXRlX2JjdWJlZF9tZXRyaWNzKGRhdGEpCgojIEV4dHJhY3QgcHJlY2lzaW9uIGFuZCByZWNhbGwgZnJvbSB0aGUgbWV0cmljcwpwcmVjaXNpb24gPC0gbWV0cmljcyRwcmVjaXNpb24KcmVjYWxsIDwtIG1ldHJpY3MkcmVjYWxsCgojIFByaW50IHRoZSByZXN1bHRzCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpCmNhdCgiQkN1YmVkIFJlY2FsbDoiLCByZWNhbGwsICJcbiIpCgpgYGAKCiMjIENsdXN0cmluZzMKCiMjIyBLLW1lYW5zCgpgYGB7cn0KIyAzLSBydW4gay1tZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgNCBjbHVzdGVycwojc2V0IGEgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uICB0byBtYWtlIHRoZSByZXN1bHRzIHJlcHJvZHVjaWJsZQpzZXQuc2VlZCg4OTUzKQprbWVhbnMucmVzdWx0IDwtIGttZWFucyhkYXRhX2Zvcl9jbHVzdGVyLDQpCiMgcHJpbnQgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0CmttZWFucy5yZXN1bHQKYGBgCgojIyMgdmlzdWFsaXplIGNsdXN0ZXJpbmcKCmBgYHtyfQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nICg0IGNsdXN0ZXJzKQoKZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhX2Zvcl9jbHVzdGVyKQpgYGAKCiMjIyBoaWVyY3JjaGljYWwgY2x1c3RlcmluZwoKYGBge3J9CiMgZHJhdyBhIHNhbXBsZSBvZiA1MCByZWNvcmRzIGZyb20gdGhlIGRhdGEsIHNvIHRoYXQgdGhlIGNsdXN0ZXJpbmcgcGxvdCB3aWxsIG5vdCBiZSBvdmVyIGNyb3dkZWQgYW5kIGVhc3kgdG8gdW5kcmVzdGFuZCAKaWR4Mzwtc2FtcGxlKDE6ZGltKGRhdGFfZm9yX2NsdXN0ZXIpWzFdLCA1MCkKc2FtcGxlX2MzPC1kYXRhX2Zvcl9jbHVzdGVyW2lkeDMsIF0KCiMjIGhpZXJjcmNoaWNhbGNsdXN0ZXJpbmcKaGMzLmN1dDwtIGhjdXQoc2FtcGxlX2MzLCBrID0gNCwgaGNfbWV0aG9kPSAiY29tcGxldGUiKQpgYGAKCiMjIyAjZGVuZHJvZ3JhbQoKZGVuZHJvZ3JhbSBpcyBhIHRyZWUgZGlhZ3JhbSB0aGF0IGRpc3BsYXlzIHRoZSBhcnJhbmdlbWVudCBvZiBkYXRhIHBvaW50cyBpbiBhIGhpZXJhcmNoaWNhbCBvcmRlciBiYXNlZCBvbiB0aGVpciBzaW1pbGFyaXR5IG9yIGRpc3NpbWlsYXJpdHkuCgpgYGB7cn0KIyBWaXN1YWxpemUgZGVuZHJvZ3JhbQpmdml6X2RlbmQoaGMzLmN1dCxyZWN0PSBUUlVFKQojIFZpc3VhbGl6ZSBjbHVzdGVyCmZ2aXpfY2x1c3RlcihoYzMuY3V0LCBlbGxpcHNlLnR5cGU9ICJjb252ZXgiKQpgYGAKCiMjIyAjYXZlcmFnZSBzaWxob3VldHRlCgpUaGlzIG1ldGhvZCBjYWxjdWxhdGVzIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgd2lkdGggZm9yIGRpZmZlcmVudCB2YWx1ZXMgb2YgaywgZGV0ZXJtaW5pbmcgaG93IHdlbGwgZGF0YSBwb2ludHMgZml0IGludG8gdGhlaXIgYXNzaWduZWQgY2x1c3RlcnMuCgpgYGB7cn0KICNhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGVhY2ggY2x1c3RlcnMgCgphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoZGF0YV9mb3JfY2x1c3RlcikpICNhIGRpc3NpbWlsYXJpdHkgb2JqZWN0IGluaGVyaXRpbmcgZnJvbSBjbGFzcyBkaXN0IG9yIGNvZXJjaWJsZSB0byBvbmUuIElmIG5vdCBzcGVjaWZpZWQsIGRtYXRyaXggbXVzdCBiZS4KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zCmBgYAoKIyMjIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbAoKQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsIGFyZSBtZXRyaWNzIHVzZWQgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcywgcGFydGljdWxhcmx5IGluIHRoZSBjb250ZXh0IG9mIGV2YWx1YXRpbmcgdGhlIHF1YWxpdHkgb2YgY2x1c3RlcmluZyBhc3NpZ25tZW50cyBmb3IgaW5kaXZpZHVhbCBkYXRhIHBvaW50cy4KCmBgYHtyfQoKY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKGttZWFucy5yZXN1bHQkY2x1c3RlcikKZ3JvdW5kX3RydXRoX2xhYmVscyA8LSBjKHdhdGVyX3BvdGFiaWxpdHkpCmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0gY2x1c3Rlcl9hc3NpZ25tZW50cywgbGFiZWwgPSBncm91bmRfdHJ1dGhfbGFiZWxzKQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCmNhbGN1bGF0ZV9iY3ViZWRfbWV0cmljcyA8LSBmdW5jdGlvbihkYXRhKSB7CiAgbiA8LSBucm93KGRhdGEpCiAgcHJlY2lzaW9uX3N1bSA8LSAwCiAgcmVjYWxsX3N1bSA8LSAwCgogIGZvciAoaSBpbiAxOm4pIHsKICAgIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldCiAgICBsYWJlbCA8LSBkYXRhJGxhYmVsW2ldCiAgICAKIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgd2l0aGluIHRoZSBzYW1lIGNsdXN0ZXIKc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGEkbGFiZWxbZGF0YSRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyBpbiB0aGUgc2FtZSBjbHVzdGVyCnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpCiAgICAKIyBDb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIHdpdGggdGhlIHNhbWUgY2F0ZWdvcnkKdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbCkKICAgIAojIENhbGN1bGF0ZSBwcmVjaXNpb24gYW5kIHJlY2FsbCBmb3IgdGhlIGN1cnJlbnQgaXRlbSBhbmQgYWRkIHRoZW0gdG8gdGhlIHN1bXMKcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgL3RvdGFsX3NhbWVfY2x1c3RlcgpyZWNhbGxfc3VtIDwtIHJlY2FsbF9zdW0gKyBzYW1lX2NhdGVnb3J5X3NhbWVfY2x1c3RlciAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkKICB9CgogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwKICBwcmVjaXNpb24gPC0gcHJlY2lzaW9uX3N1bSAvIG4KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4KCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQp9CgojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YSkKCiMgRXh0cmFjdCBwcmVjaXNpb24gYW5kIHJlY2FsbCBmcm9tIHRoZSBtZXRyaWNzCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbgpyZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJCQ3ViZWQgUHJlY2lzaW9uOiIsIHByZWNpc2lvbiwgIlxuIikKY2F0KCJCQ3ViZWQgUmVjYWxsOiIsIHJlY2FsbCwgIlxuIikKCmBgYAoKIyMgQ2x1c3RyaW5nNAoKIyMjIyBLLW1lYW5zIHJ1biBrLW1lYW5zIGNsdXN0ZXJpbmcgdG8gZmluZCA1IGNsdXN0ZXJzCgpgYGB7cn0KI3NldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiAgdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUKc2V0LnNlZWQoODk1MykKa21lYW5zLnJlc3VsdCA8LSBrbWVhbnMoZGF0YV9mb3JfY2x1c3Rlciw1KQojIHByaW50IHRoZSBjbHVzdGVybmcgcmVzdWx0CmttZWFucy5yZXN1bHQKYGBgCgojIyMgdmlzdWFsaXplIGNsdXN0ZXJpbmcKCmBgYHtyfQojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nICg1IGNsdXN0ZXJzKQoKZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBkYXRhX2Zvcl9jbHVzdGVyKQpgYGAKCiMjIyBoaWVyY3JjaGljYWwgY2x1c3RlcmluZwoKYGBge3J9CiMgZHJhdyBhIHNhbXBsZSBvZiA1MCByZWNvcmRzIGZyb20gdGhlIGRhdGEsIHNvIHRoYXQgdGhlIGNsdXN0ZXJpbmcgcGxvdCB3aWxsIG5vdCBiZSBvdmVyIGNyb3dkZWQgYW5kIGVhc3kgdG8gdW5kcmVzdGFuZCAKaWR4NDwtc2FtcGxlKDE6ZGltKGRhdGFfZm9yX2NsdXN0ZXIpWzFdLCA1MCkKc2FtcGxlX2M0PC1kYXRhX2Zvcl9jbHVzdGVyW2lkeDQsIF0KCiMjIGhpZXJjcmNoaWNhbGNsdXN0ZXJpbmcKaGM0LmN1dDwtIGhjdXQoc2FtcGxlX2M0LCBrID0gNSwgaGNfbWV0aG9kPSAiY29tcGxldGUiKQpgYGAKCiMjIyBkZW5kcm9ncmFtCgpkZW5kcm9ncmFtIGlzIGEgdHJlZSBkaWFncmFtIHRoYXQgZGlzcGxheXMgdGhlIGFycmFuZ2VtZW50IG9mIGRhdGEgcG9pbnRzIGluIGEgaGllcmFyY2hpY2FsIG9yZGVyIGJhc2VkIG9uIHRoZWlyIHNpbWlsYXJpdHkgb3IgZGlzc2ltaWxhcml0eS4KCmBgYHtyfQojIFZpc3VhbGl6ZSBkZW5kcm9ncmFtCmZ2aXpfZGVuZChoYzQuY3V0LHJlY3Q9IFRSVUUpCiMgVmlzdWFsaXplIGNsdXN0ZXIKZnZpel9jbHVzdGVyKGhjNC5jdXQsIGVsbGlwc2UudHlwZT0gImNvbnZleCIpCmBgYAoKIyMjICNhdmVyYWdlIHNpbGhvdWV0dGUKClRoaXMgbWV0aG9kIGNhbGN1bGF0ZXMgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aCBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiBrLCBkZXRlcm1pbmluZyBob3cgd2VsbCBkYXRhIHBvaW50cyBmaXQgaW50byB0aGVpciBhc3NpZ25lZCBjbHVzdGVycy4gZGV0ZXJtaW5pbmcgaG93IHdlbGwgZGF0YSBwb2ludHMgZml0IGludG8gdGhlaXIgYXNzaWduZWQgY2x1c3RlcnMuCgpgYGB7cn0KICNhdmVyYWdlIHNpbGhvdWV0dGUgZm9yIGVhY2ggY2x1c3RlcnMgCgphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLGRpc3QoZGF0YV9mb3JfY2x1c3RlcikpICNhIGRpc3NpbWlsYXJpdHkgb2JqZWN0IGluaGVyaXRpbmcgZnJvbSBjbGFzcyBkaXN0IG9yIGNvZXJjaWJsZSB0byBvbmUuIElmIG5vdCBzcGVjaWZpZWQsIGRtYXRyaXggbXVzdCBiZS4KZnZpel9zaWxob3VldHRlKGF2Z19zaWwpI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zCmBgYAoKIyMjIEJDdWJlZCBwcmVjaXNpb24gYW5kIHJlY2FsbAoKQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsIGFyZSBtZXRyaWNzIHVzZWQgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcywgcGFydGljdWxhcmx5IGluIHRoZSBjb250ZXh0IG9mIGV2YWx1YXRpbmcgdGhlIHF1YWxpdHkgb2YgY2x1c3RlcmluZyBhc3NpZ25tZW50cyBmb3IgaW5kaXZpZHVhbCBkYXRhIHBvaW50cy4KCmBgYHtyfQoKY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBjKGttZWFucy5yZXN1bHQkY2x1c3RlcikKZ3JvdW5kX3RydXRoX2xhYmVscyA8LSBjKHdhdGVyX3BvdGFiaWxpdHkpCmRhdGEgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0gY2x1c3Rlcl9hc3NpZ25tZW50cywgbGFiZWwgPSBncm91bmRfdHJ1dGhfbGFiZWxzKQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbiBhbmQgcmVjYWxsCmNhbGN1bGF0ZV9iY3ViZWRfbWV0cmljcyA8LSBmdW5jdGlvbihkYXRhKSB7CiAgbiA8LSBucm93KGRhdGEpCiAgcHJlY2lzaW9uX3N1bSA8LSAwCiAgcmVjYWxsX3N1bSA8LSAwCgogIGZvciAoaSBpbiAxOm4pIHsKICAgIGNsdXN0ZXIgPC0gZGF0YSRjbHVzdGVyW2ldCiAgICBsYWJlbCA8LSBkYXRhJGxhYmVsW2ldCiAgICAKIyBDb3VudCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGZyb20gdGhlIHNhbWUgY2F0ZWdvcnkgd2l0aGluIHRoZSBzYW1lIGNsdXN0ZXIKc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgPC0gc3VtKGRhdGEkbGFiZWxbZGF0YSRjbHVzdGVyID09IGNsdXN0ZXJdID09IGxhYmVsKQogICAgCiMgQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBpdGVtcyBpbiB0aGUgc2FtZSBjbHVzdGVyCnRvdGFsX3NhbWVfY2x1c3RlciA8LSBzdW0oZGF0YSRjbHVzdGVyID09IGNsdXN0ZXIpCiAgICAKIyBDb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIHdpdGggdGhlIHNhbWUgY2F0ZWdvcnkKdG90YWxfc2FtZV9jYXRlZ29yeSA8LSBzdW0oZGF0YSRsYWJlbCA9PSBsYWJlbCkKICAgIAojIENhbGN1bGF0ZSBwcmVjaXNpb24gYW5kIHJlY2FsbCBmb3IgdGhlIGN1cnJlbnQgaXRlbSBhbmQgYWRkIHRoZW0gdG8gdGhlIHN1bXMKcHJlY2lzaW9uX3N1bSA8LSBwcmVjaXNpb25fc3VtICsgc2FtZV9jYXRlZ29yeV9zYW1lX2NsdXN0ZXIgL3RvdGFsX3NhbWVfY2x1c3RlcgpyZWNhbGxfc3VtIDwtIHJlY2FsbF9zdW0gKyBzYW1lX2NhdGVnb3J5X3NhbWVfY2x1c3RlciAvIHRvdGFsX3NhbWVfY2F0ZWdvcnkKICB9CgogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcHJlY2lzaW9uIGFuZCByZWNhbGwKICBwcmVjaXNpb24gPC0gcHJlY2lzaW9uX3N1bSAvIG4KICByZWNhbGwgPC0gcmVjYWxsX3N1bSAvIG4KCiAgcmV0dXJuKGxpc3QocHJlY2lzaW9uID0gcHJlY2lzaW9uLCByZWNhbGwgPSByZWNhbGwpKQp9CgojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uIGFuZCByZWNhbGwKbWV0cmljcyA8LSBjYWxjdWxhdGVfYmN1YmVkX21ldHJpY3MoZGF0YSkKCiMgRXh0cmFjdCBwcmVjaXNpb24gYW5kIHJlY2FsbCBmcm9tIHRoZSBtZXRyaWNzCnByZWNpc2lvbiA8LSBtZXRyaWNzJHByZWNpc2lvbgpyZWNhbGwgPC0gbWV0cmljcyRyZWNhbGwKCiMgUHJpbnQgdGhlIHJlc3VsdHMKY2F0KCJCQ3ViZWQgUHJlY2lzaW9uOiIsIHByZWNpc2lvbiwgIlxuIikKY2F0KCJCQ3ViZWQgUmVjYWxsOiIsIHJlY2FsbCwgIlxuIikKCmBgYAoKIyMjIEVsYm93IG1ldGhvZAoKZWxib3cgbWV0aG9kIGhlbHBzIGZpbmQgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIChrKSBpbiBrLW1lYW5zIGNsdXN0ZXJpbmcuIHRoZSAiZWxib3ciIHBvaW50IGlzIHdoZXJlIHRoZSByYXRlIG9mIGRlY3JlYXNlIGluIFdTUyBzbG93cywgaW5kaWNhdGluZyBhIGdvb2QgYmFsYW5jZSBiZXR3ZWVuIGNsdXN0ZXIgY291bnQgYW5kIGNsdXN0ZXIgY29tcGFjdG5lc3MuIFRoZSBnb2FsIGlzIHRvIHNlbGVjdCB0aGUgc21hbGxlc3QgayB0aGF0IHJldGFpbnMgbW9zdCBvZiB0aGUgZGF0YSdzIHZhcmlhYmlsaXR5LgoKYGBge3J9CiMgMy0gRWxib3cgbWV0aG9kCiNmdml6X25iY2x1c3QoKSB3aXRoIHdpdGhpbiBjbHVzdGVyIHN1bXMgb2Ygc3F1YXJlcyAod3NzKSBtZXRob2QKIApmdml6X25iY2x1c3QoZGF0YV9mb3JfY2x1c3Rlciwga21lYW5zLCBtZXRob2QgPSAid3NzIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGxpbmV0eXBlID0gMikrCiAgbGFicyhzdWJ0aXRsZSA9ICJFbGJvdyBtZXRob2QiKQpgYGAKCiMjIyBUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyBmb3IgawoKV1NTIGlzIHRvIGdpdmUgeW91IGFuIGluZGljYXRpb24gb2YgaG93IHdlbGwgdGhlIGRhdGEgY2FuIGJlIHJlcHJlc2VudGVkIGJ5IGEgY2VydGFpbiBudW1iZXIgb2YgY2x1c3RlcnMuIEluIGstbWVhbnMgY2x1c3RlcmluZywgdHlwaWNhbGx5IGNob29zZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIChrKSB0aGF0IG1pbmltaXplcyB0aGlzIHRvdGFsIFdTUy4KCmBgYHtyfQoKZm9yIChrIGluIDI6NSkgewogIGttZWFuc19yZXN1bHQgPC0ga21lYW5zKHdhdGVyX3BvdGFiaWxpdHksIGNlbnRlcnMgPSBrKQogIHRvdGFsX3dpdGhpbnNzIDwtIGttZWFuc19yZXN1bHQkdG90LndpdGhpbnNzCiAgY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyBmb3IgayA9IiwgaywgIjoiLCB0b3RhbF93aXRoaW5zcywgIlxuIikKfQoKYGBgCgojIyMgQ29tcGFyaXNvbiBDcml0ZXJpYToKCnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQ2x1c3RyaW5nMSB8IENsdXN0cmluZzIgIHwgQ2x1c3RyaW5nMyAgfCBDbHVzdHJpbmc0ICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18CnwgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIHwgMC4wOCAgICAgICB8IDAuMDggICAgICAgIHwgMC4wNyAgICAgICAgfCAwLjA4ICAgICAgICB8CnwgQkN1YmVkIFByZWNpc2lvbiAgICAgICAgIHwgMC4wMDUzNTA3ICB8IDAuMDA1NDE3MTMyIHwgMC4wMDU0NjY2NTYgfCAwLjAwNTUyNTI3OCB8CnwgQkN1YmVkIFJlY2FsbCAgICAgICAgICAgIHwgMC45NTAxMjA5ICB8IDAuOTMzNDc0MSAgIHwgMC45MjUwNzM2ICAgfCAwLjkyMDE1NjggICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICB8ICAgICAgICAgICAgIHwgICAgICAgICAgICAgfCAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICB8ICAgICAgICAgICAgIHwgICAgICAgICAgICAgfCAgICAgICAgICAgICB8CgpUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyBmb3IgayA9IDIgOiAzNTQzNTczNjE3OAoKVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXMgZm9yIGsgPSAzIDogMTgwMDU2MzYyMDcKClRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzIGZvciBrID0gNCA6IDEwNjQ0Njg2OTEzCgpUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyBmb3IgayA9IDUgOiA3MjYzMTUwODc2Cg==